├── .gitignore ├── CAD ├── AddPartToAssembly.cs ├── AssemblyMoveComponent.cs ├── CreateCsysFromDatum.cs ├── SaveCopy.cs ├── SetCGMForPartTC.cs ├── SetCGMToAllPartsInFolder.cs └── SphereUpdateColor.cs ├── NXOpen-CAE.sln ├── NXOpenCAE.csproj ├── PostProcessing ├── ExportAllSolutions.cs ├── ExportGroupsAsUnv.cs ├── ExportShellOrientationAsUnv.cs ├── ExportShellThicknessAsUnv.cs ├── ListNodalResults.cs ├── PostProcessing.cs ├── PostProcessing1980.cs └── ScreenShotCreator.cs ├── PreProcessing ├── AddRelatedNodesAndElements.cs ├── ChangeMaterial.cs ├── CreateMeshCollector.cs ├── CreateNodes.cs ├── ForceBC.cs ├── ListBucklingDirections.cs ├── RBE3Creation.cs ├── RepositionUsingExpressions.cs ├── SolutionSubcase.cs └── SolveSolution.cs ├── Program.cs ├── README.md ├── Tools ├── CreateGroupsFromCAD.cs ├── Excel │ ├── README.md │ ├── ReadExcel.cs │ ├── ReadExcelOleDb.cs │ ├── ReadExcelReflection.cs │ └── Sample1.xlsx ├── PointsToCsv.cs ├── PrintAssemblyStructure.cs └── VectorArithmetic.cs └── license.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Visual Studio Code cache/options directory 2 | .vscode/ 3 | 4 | # No SimCenter dll's because of copyright 5 | NXOpen*.dll 6 | 7 | # No windows dlls, just for intellisense 8 | *.dll 9 | 10 | # Build results 11 | [Bb]in/ 12 | [Oo]bj/ -------------------------------------------------------------------------------- /CAD/AddPartToAssembly.cs: -------------------------------------------------------------------------------- 1 | namespace TheScriptingEngineer 2 | { 3 | using System; 4 | using System.IO; // for path operations 5 | using System.Collections.Generic; // for lists 6 | using NXOpen; // so we can use NXOpen functionality 7 | using NXOpen.CAE; // so we don't need to start everything with NXOpen.CAE 8 | using NXOpenUI; 9 | using NXOpen.UF; 10 | using NXOpen.Utilities; 11 | 12 | public class AddPartToAssembly 13 | { 14 | // global variables used throughout 15 | public static Session theSession = Session.GetSession(); 16 | public static ListingWindow theLW = theSession.ListingWindow; 17 | public static BasePart basePart = theSession.Parts.BaseWork; 18 | 19 | public static void Main(string[] args) 20 | { 21 | // entrypoint for NX 22 | theLW.Open(); 23 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 24 | 25 | String fileName = "LocationWithFullPath"; // full path of the existing file 26 | String referenceSetName = "referenceSetName"; // The name of the reference set used to represent the new component 27 | String componentName = "componentName"; // The name of the new component 28 | Int16 layer = 1; // The layer to place the new component on. 29 | // -1 means use the original layers defined in the component. 0 means use the work layer. 1-256 means use the specified layer. 30 | Point3d basePoint = new Point3d(0, 0, 0); // Location of the new component 31 | Matrix3x3 orientation; 32 | orientation.Xx = 1; 33 | orientation.Xy = 0; 34 | orientation.Xz = 0; 35 | orientation.Yx = 0; 36 | orientation.Yy = 1; 37 | orientation.Yz = 0; 38 | orientation.Zx = 0; 39 | orientation.Zy = 0; 40 | orientation.Zz = 1; 41 | 42 | Part Assembly = (Part)basePart; 43 | PartLoadStatus partLoadStatus; 44 | Assembly.ComponentAssembly.AddComponent(fileName, referenceSetName, componentName, basePoint, orientation, layer, out partLoadStatus); 45 | 46 | Assembly.Save(BasePart.SaveComponents.True, BasePart.CloseAfterSave.False); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /CAD/AssemblyMoveComponent.cs: -------------------------------------------------------------------------------- 1 | namespace TheScriptingEngineer 2 | { 3 | using System; 4 | using System.IO; // for path operations 5 | using System.Collections.Generic; // for using list 6 | using NXOpen; 7 | using NXOpen.CAE; // so we don't need to start everything with NXOpen.CAE 8 | using NXOpen.Utilities; 9 | using NXOpen.UF; 10 | using NXOpenUI; 11 | 12 | public class AssemblyMoveComponent 13 | { 14 | static NXOpen.Session theSession = NXOpen.Session.GetSession(); 15 | static ListingWindow theLW = theSession.ListingWindow; 16 | static BasePart basePart = theSession.Parts.BaseWork; 17 | public static void Main(string[] args) 18 | { 19 | theLW.Open(); 20 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 21 | theLW.WriteFullline("Funcitons RotateComponentY and RotateComponentX are UNCHECKED use at own risk"); 22 | 23 | NXOpen.Part workPart = (NXOpen.Part)basePart; 24 | NXOpen.Assemblies.Component component1 = ((NXOpen.Assemblies.Component)workPart.ComponentAssembly.RootComponent.FindObject("COMPONENT model1 1")); 25 | 26 | //TranslateComponent(workPart, component1, 0, -100, 0); 27 | RotateComponentZ(workPart, component1, -30); 28 | 29 | } 30 | 31 | /// 32 | /// Translates a component in a part. 33 | /// 34 | /// The part in which to move the component. 35 | /// The component to translate. 36 | /// Distance to translate in X-direction. 37 | /// Distance to translate in Y-direction. 38 | /// Distance to translate in Z-direction. 39 | public static void TranslateComponent(Part workPart, NXOpen.Assemblies.Component componentToTranslate, double dX, double dY, double dZ) 40 | { 41 | // now that we have the component, so we can move it 42 | Vector3d translate; 43 | translate.X = dX; 44 | translate.Y = dY; 45 | translate.Z = dZ; 46 | Matrix3x3 rotate; 47 | rotate.Xx = 1; 48 | rotate.Xy = 0; 49 | rotate.Xz = 0; 50 | rotate.Yx = 0; 51 | rotate.Yy = 1; 52 | rotate.Yz = 0; 53 | rotate.Zx = 0; 54 | rotate.Zy = 0; 55 | rotate.Zz = 1; 56 | workPart.ComponentAssembly.MoveComponent(componentToTranslate, translate, rotate); 57 | } 58 | 59 | 60 | /// 61 | /// Rotates a component in an assembly around the global z-axis. 62 | /// 63 | /// The part in which to move the component. 64 | /// The component to translate. 65 | /// Angle to rotate around z-axis in degrees. 66 | public static void RotateComponentZ(Part workPart, NXOpen.Assemblies.Component componentToRotate, double angle) 67 | { 68 | // now that we have the component, so we can move it 69 | Vector3d translate; 70 | translate.X = 0; 71 | translate.Y = 0; 72 | translate.Z = 0; 73 | Matrix3x3 rotate; 74 | rotate.Xx = System.Math.Cos(angle / 180 * System.Math.PI); 75 | rotate.Xy = -System.Math.Sin(angle / 180 * System.Math.PI); 76 | rotate.Xz = 0; 77 | rotate.Yx = System.Math.Sin(angle / 180 * System.Math.PI); 78 | rotate.Yy = System.Math.Cos(angle / 180 * System.Math.PI); 79 | rotate.Yz = 0; 80 | rotate.Zx = 0; 81 | rotate.Zy = 0; 82 | rotate.Zz = 1; 83 | workPart.ComponentAssembly.MoveComponent(componentToRotate, translate, rotate); 84 | } 85 | 86 | 87 | /// 88 | /// Rotates a component in an assembly around the global y-axis. 89 | /// 90 | /// The part in which to move the component. 91 | /// The component to translate. 92 | /// Angle to rotate around y-axis in degrees. 93 | public static void RotateComponentY(Part workPart, NXOpen.Assemblies.Component componentToRotate, double angle) 94 | { 95 | // now that we have the component, so we can move it 96 | Vector3d translate; 97 | translate.X = 0; 98 | translate.Y = 0; 99 | translate.Z = 0; 100 | Matrix3x3 rotate; 101 | rotate.Xx = System.Math.Cos(angle / 180 * System.Math.PI); 102 | rotate.Xy = 0; 103 | rotate.Xz = -System.Math.Sin(angle / 180 * System.Math.PI); 104 | rotate.Yx = 0; 105 | rotate.Yy = 1; 106 | rotate.Yz = 0; 107 | rotate.Zx = System.Math.Sin(angle / 180 * System.Math.PI); 108 | rotate.Zy = 0; 109 | rotate.Zz = System.Math.Cos(angle / 180 * System.Math.PI); 110 | workPart.ComponentAssembly.MoveComponent(componentToRotate, translate, rotate); 111 | } 112 | 113 | 114 | /// 115 | /// Rotates a component in an assembly around the global x-axis. 116 | /// 117 | /// The part in which to move the component. 118 | /// The component to translate. 119 | /// Angle to rotate around x-axis in degrees. 120 | public static void RotateComponentX(Part workPart, NXOpen.Assemblies.Component componentToRotate, double angle) 121 | { 122 | // now that we have the component, so we can move it 123 | Vector3d translate; 124 | translate.X = 0; 125 | translate.Y = 0; 126 | translate.Z = 0; 127 | Matrix3x3 rotate; 128 | rotate.Xx = 1; 129 | rotate.Xy = 0; 130 | rotate.Xz = 0; 131 | rotate.Yx = 0; 132 | rotate.Yy = System.Math.Cos(angle / 180 * System.Math.PI); 133 | rotate.Yz = -System.Math.Sin(angle / 180 * System.Math.PI); 134 | rotate.Zx = 0; 135 | rotate.Zy = System.Math.Sin(angle / 180 * System.Math.PI); 136 | rotate.Zz = System.Math.Cos(angle / 180 * System.Math.PI); 137 | workPart.ComponentAssembly.MoveComponent(componentToRotate, translate, rotate); 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /CAD/CreateCsysFromDatum.cs: -------------------------------------------------------------------------------- 1 | namespace TheScriptingEngineer 2 | { 3 | using System; 4 | using System.IO; // for path operations 5 | using System.Collections.Generic; // for using list 6 | using NXOpen; 7 | using NXOpen.CAE; // so we don't need to start everything with NXOpen.CAE 8 | using NXOpen.Utilities; 9 | using NXOpen.UF; 10 | using NXOpenUI; 11 | 12 | public class CsysFromDatum 13 | { 14 | static NXOpen.Session theSession = NXOpen.Session.GetSession(); 15 | static ListingWindow theLW = theSession.ListingWindow; 16 | static BasePart basePart = theSession.Parts.BaseWork; 17 | 18 | public static void Main(string[] args) 19 | { 20 | theLW.Open(); 21 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 22 | } 23 | 24 | /// 25 | /// Creates a Cartesion coordinates system using a datumPlane as input. 26 | /// The orthogonal vectors are constructed in the plane to define the coordinate system, using basic geometry. 27 | /// 28 | /// Instance of the datumPlane to create the coordinate system for. 29 | /// A coordinate system which has the same origin as the plane with x and y axis in the plane. 30 | public static CartesianCoordinateSystem CreateCsysFromDatum(DatumPlane datumPlane) 31 | { 32 | Point3d origin = datumPlane.Origin; 33 | Vector3d normal = datumPlane.Normal; 34 | 35 | NXOpen.Vector3d global = new Vector3d(1, 0, 0); 36 | double projection = Math.Abs(normal.X * global.X + normal.Y * global.Y + normal.Z * global.Z); 37 | if (projection >= 0.999) 38 | { 39 | global.X = 0; 40 | global.Y = 1; 41 | global.Z = 0; 42 | } 43 | 44 | // we first project the global onto the plane normal 45 | // then subtract to get the component of global IN the plane which will be are local axis in the recipe definition 46 | double projectionMagnitude = global.X * normal.X + global.Y * normal.Y + global.Z * normal.Z; 47 | NXOpen.Vector3d globalOnNormal = new Vector3d(projectionMagnitude * normal.X, projectionMagnitude * normal.Y, projectionMagnitude * normal.Z); 48 | NXOpen.Vector3d globalOnPlane = new Vector3d(global.X - globalOnNormal.X, global.Y - globalOnNormal.Y, global.Z - globalOnNormal.Z); 49 | 50 | // normalize 51 | double magnitude = Math.Sqrt(globalOnPlane.X * globalOnPlane.X + globalOnPlane.Y * globalOnPlane.Y + globalOnPlane.Z * globalOnPlane.Z); 52 | globalOnPlane = new Vector3d(globalOnPlane.X / magnitude, globalOnPlane.Y / magnitude, globalOnPlane.Z / magnitude); 53 | 54 | // cross product of globalOnPlane and normal give vector in plane, otrhogonal to globalOnPlane 55 | NXOpen.Vector3d globalOnPlaneNormal = new Vector3d(normal.Y * globalOnPlane.Z - normal.Z * globalOnPlane.Y, 56 | -normal.X * globalOnPlane.Z + normal.Z * globalOnPlane.X, 57 | normal.X * globalOnPlane.Y - normal.Y * globalOnPlane.X); 58 | 59 | magnitude = Math.Sqrt(globalOnPlaneNormal.X * globalOnPlaneNormal.X + globalOnPlaneNormal.Y * globalOnPlaneNormal.Y + globalOnPlaneNormal.Z * globalOnPlaneNormal.Z); 60 | globalOnPlaneNormal = new Vector3d(globalOnPlaneNormal.X / magnitude, globalOnPlaneNormal.Y / magnitude, globalOnPlaneNormal.Z / magnitude); 61 | 62 | NXOpen.Xform xform = basePart.Xforms.CreateXform(origin, globalOnPlane, globalOnPlaneNormal, NXOpen.SmartObject.UpdateOption.AfterModeling, 1.0); 63 | NXOpen.CartesianCoordinateSystem coordinateSystem = basePart.CoordinateSystems.CreateCoordinateSystem(xform, NXOpen.SmartObject.UpdateOption.AfterModeling); 64 | 65 | return coordinateSystem; 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /CAD/SaveCopy.cs: -------------------------------------------------------------------------------- 1 | namespace TheScriptingEngineer 2 | { 3 | using System; 4 | using System.IO; // for path operations 5 | using System.Collections.Generic; // for lists 6 | using NXOpen; // so we can use NXOpen functionality 7 | using NXOpen.CAE; // so we don't need to start everything with NXOpen.CAE 8 | using NXOpenUI; 9 | using NXOpen.UF; 10 | using NXOpen.Utilities; 11 | 12 | public class SaveCopyClass 13 | { 14 | // global variables used throughout 15 | public static Session theSession = Session.GetSession(); 16 | public static UFSession theUFSession = UFSession.GetUFSession(); 17 | public static ListingWindow theLW = theSession.ListingWindow; 18 | public static BasePart basePart = theSession.Parts.BaseWork; 19 | 20 | public static void Main(string[] args) 21 | { 22 | // entrypoint for NX 23 | theLW.Open(); 24 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 25 | 26 | string newFileName = "D:\\Temp\\newFileName.prt"; 27 | SaveAsSwitchToOriginal(basePart, newFileName); 28 | } 29 | 30 | public static void SaveCopy(NXOpen.BasePart basePart, String newFileName) 31 | { 32 | NXOpen.PartSaveStatus partSaveStatus; 33 | partSaveStatus = basePart.SaveAs(newFileName); 34 | partSaveStatus.Dispose(); 35 | 36 | theSession.Parts.CloseAll(NXOpen.BasePart.CloseModified.CloseModified, null); 37 | } 38 | 39 | public static void SaveAsSwitchToOriginal(BasePart basePart, String newFileName) 40 | { 41 | String fullPathOriginal = basePart.FullPath; 42 | SaveCopy(basePart, newFileName); 43 | 44 | PartLoadStatus partLoadStatus; 45 | basePart = theSession.Parts.OpenActiveDisplay(fullPathOriginal, DisplayPartOption.ReplaceExisting, out partLoadStatus); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /CAD/SetCGMForPartTC.cs: -------------------------------------------------------------------------------- 1 | namespace TheScriptingEngineer 2 | { 3 | using System; 4 | using System.IO; // for path operations 5 | using System.Collections.Generic; // for lists 6 | using NXOpen; // so we can use NXOpen functionality 7 | using NXOpenUI; 8 | using NXOpen.UF; 9 | using NXOpen.Utilities; 10 | using NXOpen.CAE; 11 | using NXOpen.CAM; 12 | 13 | public class SetCGMForPartTC 14 | { 15 | // global variables used throughout 16 | public static Session theSession = Session.GetSession(); 17 | public static UFSession theUFSession = UFSession.GetUFSession(); 18 | public static ListingWindow theLW = theSession.ListingWindow; 19 | public static BasePart basePart = theSession.Parts.BaseWork; 20 | 21 | public static void Main(string[] args) 22 | { 23 | // entrypoint for NX 24 | theLW.Open(); 25 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 26 | 27 | // login to teamcenter (likely not required if executing from the GUI eg. Alt + F8 -> run this file) 28 | string[] teamcenterCredentials = new string[] { "-pim=yes", "-u=username", "-p=password" }; 29 | theUFSession.Ugmgr.Initialize(teamcenterCredentials.Length, args); 30 | theUFSession.UF.IsUgmanagerActive(out bool isConnected); 31 | if (!isConnected) 32 | { 33 | throw new Exception("Invalid credentials."); 34 | } 35 | 36 | // delcare a list of files with revision you want to process (ABC: partnumber; 123 partrevision) 37 | Dictionary partNumbersAndRevision = new Dictionary(){ { "ABC", "123" }, { "DEF", "456" } }; 38 | 39 | theLW.WriteFullline("The following parts have been processed:"); 40 | foreach (KeyValuePair file in partNumbersAndRevision) 41 | { 42 | theUFSession.Ui.SetStatus("Processing file " + file); 43 | // https://community.sw.siemens.com/s/question/0D54O00007FewyGSAR/error-opening-existing-part-from-teamcenter 44 | UFSession.GetUFSession().Ugmgr.EncodePartFilename(file.Key, file.Value, null, null, out string encodedName); 45 | PartLoadStatus loadStatus; 46 | Part part = Session.GetSession().Parts.OpenDisplay(encodedName, out loadStatus); 47 | part.SaveOptions.DrawingCgmData = true; 48 | part.SaveOptions.PatternDataToSave = PartSaveOptions.PatternData.SaveNoShadedOrPattern; 49 | part.Save(BasePart.SaveComponents.True, BasePart.CloseAfterSave.True); 50 | 51 | // print the file which has been processed 52 | theLW.WriteFullline(file.Key + " " + file.Value); 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /CAD/SetCGMToAllPartsInFolder.cs: -------------------------------------------------------------------------------- 1 | namespace TheScriptingEngineer 2 | { 3 | using System; 4 | using System.IO; // for path operations 5 | using System.Collections.Generic; // for lists 6 | using NXOpen; // so we can use NXOpen functionality 7 | using NXOpenUI; 8 | using NXOpen.UF; 9 | using NXOpen.Utilities; 10 | 11 | public class SetCGMToAllPartsInFolder 12 | { 13 | // global variables used throughout 14 | public static Session theSession = Session.GetSession(); 15 | public static UFSession theUFSession = UFSession.GetUFSession(); 16 | public static ListingWindow theLW = theSession.ListingWindow; 17 | public static BasePart basePart = theSession.Parts.BaseWork; 18 | 19 | public static void Main(string[] args) 20 | { 21 | // entrypoint for NX 22 | theLW.Open(); 23 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 24 | 25 | string[] teamcenterCredentials = new string[] { "-pim=yes", "-u=username", "-p=password" }; 26 | theUFSession.Ugmgr.Initialize(teamcenterCredentials.Length, args); 27 | theUFSession.UF.IsUgmanagerActive(out bool isConnected); 28 | if (!isConnected) 29 | { 30 | throw new Exception("Invalid credentials."); 31 | } 32 | 33 | // update your path here with the path containing the files. 34 | string path = "c:\\temp"; 35 | 36 | string[] prtFiles = GetPrtFiles(path); 37 | theLW.WriteFullline("The following files have been processed:"); 38 | foreach (string file in prtFiles) 39 | { 40 | theUFSession.Ui.SetStatus("Processing file " + file); 41 | PartLoadStatus loadStatus; 42 | Part part = theSession.Parts.Open(file, out loadStatus); 43 | part.SaveOptions.DrawingCgmData = true; 44 | part.SaveOptions.PatternDataToSave = PartSaveOptions.PatternData.SaveNoShadedOrPattern; 45 | part.Save(BasePart.SaveComponents.True, BasePart.CloseAfterSave.True); 46 | theLW.WriteFullline(file); 47 | } 48 | } 49 | 50 | 51 | public static string[] GetPrtFiles(string folderPath) 52 | { 53 | try 54 | { 55 | // Check if the folder exists 56 | if (Directory.Exists(folderPath)) 57 | { 58 | // Get all files with a .prt extension in the folder 59 | string[] prtFiles = Directory.GetFiles(folderPath, "*.prt"); 60 | 61 | if (prtFiles.Length > 0) 62 | { 63 | theLW.WriteFullline("List of .prt files:"); 64 | 65 | foreach (string prtFile in prtFiles) 66 | { 67 | theLW.WriteFullline(prtFile); 68 | } 69 | 70 | return prtFiles; 71 | } 72 | else 73 | { 74 | theLW.WriteFullline("No .prt files found in the specified folder."); 75 | return null; 76 | } 77 | } 78 | else 79 | { 80 | theLW.WriteFullline("The specified folder does not exist."); 81 | return null; 82 | } 83 | } 84 | catch (Exception ex) 85 | { 86 | Console.WriteLine($"An error occurred: {ex.Message}"); 87 | return null; 88 | } 89 | } 90 | 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /CAD/SphereUpdateColor.cs: -------------------------------------------------------------------------------- 1 |  2 | using NXOpen; // so we can use NXOpen functionality 3 | 4 | public class Sphere 5 | { 6 | // global variables used throughout 7 | public static Session theSession = Session.GetSession(); 8 | public static ListingWindow theLW = theSession.ListingWindow; 9 | public static BasePart basePart = theSession.Parts.BaseWork; 10 | 11 | public static void Main(string[] args) 12 | { 13 | // entrypoint for NX 14 | theLW.Open(); 15 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 16 | 17 | NXOpen.Features.SphereBuilder sphereBuilder1 = basePart.Features.CreateSphereBuilder(null); 18 | sphereBuilder1.Diameter.SetFormula("100"); 19 | 20 | NXOpen.Features.Sphere sphere = (NXOpen.Features.Sphere)sphereBuilder1.Commit(); 21 | theLW.WriteFullline(sphere.GetType().ToString()); 22 | 23 | Body[] bodies = sphere.GetBodies(); 24 | foreach (Body body in sphere.GetBodies()) 25 | { 26 | NXOpen.DisplayModification displayModification = theSession.DisplayManager.NewDisplayModification(); 27 | displayModification.ApplyToAllFaces = true; 28 | displayModification.ApplyToOwningParts = false; 29 | displayModification.NewColor = 111; 30 | displayModification.Apply(new DisplayableObject[]{body}); 31 | displayModification.Dispose(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /NXOpen-CAE.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.002.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NXOpenCAE", "NXOpenCAE.csproj", "{EC5C4A49-3AF7-4329-A694-911CEC192482}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {EC5C4A49-3AF7-4329-A694-911CEC192482}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {EC5C4A49-3AF7-4329-A694-911CEC192482}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {EC5C4A49-3AF7-4329-A694-911CEC192482}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {EC5C4A49-3AF7-4329-A694-911CEC192482}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {06723049-FBC5-457C-8FCF-A202EED39CE6} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /NXOpenCAE.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | library 5 | netcoreapp3.1 6 | enable 7 | true 8 | 9 | 10 | 11 | 12 | ..\dlls\NXOpen.dll 13 | 14 | 15 | ..\dlls\NXOpen.Utilities.dll 16 | 17 | 18 | ..\dlls\NXOpen.UF.dll 19 | 20 | 21 | ..\dlls\NXOpenUI.dll 22 | 23 | 24 | 25 | 39 | 40 | 41 | 42 | ..\dlls\System.Windows.Forms.dll 43 | 44 | 45 | ..\dlls\System.Drawing.dll 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /PostProcessing/ExportGroupsAsUnv.cs: -------------------------------------------------------------------------------- 1 | namespace TheScriptingEngineer 2 | { 3 | using System; 4 | using System.IO; // for path operations 5 | using System.Collections.Generic; // for lists 6 | using NXOpen; // so we can use NXOpen functionality 7 | using NXOpen.CAE; // so we don't need to start everything with NXOpen.CAE 8 | using NXOpenUI; 9 | using NXOpen.UF; 10 | using NXOpen.Utilities; 11 | 12 | public class ExportGroupsAsUnv 13 | { 14 | // global variables used throughout 15 | public static Session theSession = Session.GetSession(); 16 | public static UFSession theUFSession = UFSession.GetUFSession(); 17 | public static ListingWindow theLW = theSession.ListingWindow; 18 | public static BasePart basePart = theSession.Parts.BaseWork; 19 | 20 | public static void Main(string[] args) 21 | { 22 | theLW.Open(); 23 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 24 | 25 | if (basePart is CaePart) 26 | { 27 | CaePart caePart = (CaePart)basePart; 28 | WriteGroups(caePart, "Groups.unv"); 29 | } 30 | else 31 | { 32 | theLW.WriteFullline("This function needs to start from a .sim, .afem or .fem."); 33 | return; 34 | } 35 | } 36 | 37 | /// 38 | /// This function writes nodes and elements in a group to a universal file. 39 | /// The content of the file are all groups and the nodes and elements in these groups. 40 | /// 41 | /// The BaseFemPart to export the groups for. 42 | /// The name of the universal file to write the groups to 43 | public static void WriteGroups(CaePart caePart, string fileName) 44 | { 45 | string dataset2429 = CreateGroupDatasets(caePart); 46 | fileName = CreateFullPath(fileName); 47 | 48 | File.WriteAllText(fileName, dataset2429); 49 | } 50 | 51 | 52 | public static string CreateGroupDatasets(CaePart caePart) 53 | { 54 | string dataset2429 = ""; 55 | foreach (CaeGroup group in caePart.CaeGroups) 56 | { 57 | theUFSession.Ui.SetStatus("Processing group " + group.Name); 58 | 59 | // add the actual nodes and elements 60 | string[] dataset = CreateGroupDataSet(caePart, group); 61 | if (dataset.Length == 0) 62 | { 63 | continue; 64 | } 65 | 66 | // add the headers for the group 67 | int nOfItems = 0; 68 | if (dataset.Length == 1) 69 | { 70 | nOfItems = dataset[0].Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Length / 2; 71 | } 72 | else 73 | { 74 | nOfItems = (dataset.Length - 1) * 4 + (dataset[dataset.Length - 1]).Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Length / 2; 75 | } 76 | dataset2429 = dataset2429 + " -1" + Environment.NewLine; 77 | dataset2429 = dataset2429 + String.Format("{0,6}", "2429") + Environment.NewLine; 78 | dataset2429 = dataset2429 + String.Format("{0,10}", group.Label) + 79 | String.Format("{0,10}", "0") + 80 | String.Format("{0,10}", "0") + 81 | String.Format("{0,10}", "0") + 82 | String.Format("{0,10}", "0") + 83 | String.Format("{0,10}", "0") + 84 | String.Format("{0,10}", "0") + 85 | String.Format("{0,10}", nOfItems) + Environment.NewLine; 86 | dataset2429 = dataset2429 + group.Name + Environment.NewLine; 87 | dataset2429 = dataset2429 + string.Join(Environment.NewLine, dataset) + Environment.NewLine; 88 | dataset2429 = dataset2429 + " -1" + Environment.NewLine; 89 | } 90 | 91 | return dataset2429; 92 | } 93 | 94 | 95 | public static string[] CreateGroupDataSet(CaePart caePart, CaeGroup group) 96 | { 97 | theLW.WriteFullline("Processing " + group.Name); 98 | List datasetItems = new List(); 99 | 100 | // Get the node labels in the group 101 | IList allNodeLabels = GetNodesInGroup(caePart, group).Keys; 102 | theLW.WriteFullline("Found " + allNodeLabels.Count + " nodes"); 103 | 104 | if (allNodeLabels.Count != 0) 105 | { 106 | foreach (int label in allNodeLabels) 107 | { 108 | datasetItems.Add(String.Format("{0,10}", 7) + String.Format("{0,10}", label)); 109 | } 110 | } 111 | 112 | // Get the element labels in the group 113 | IList allElementLabels = GetElementsInGroup(caePart, group).Keys; 114 | theLW.WriteFullline("Found " + allElementLabels.Count + " elements"); 115 | 116 | if (allElementLabels.Count != 0) 117 | { 118 | foreach (int label in allElementLabels) 119 | { 120 | datasetItems.Add(String.Format("{0,10}", 8) + String.Format("{0,10}", label)); 121 | } 122 | } 123 | 124 | // More items could be implemented here (eg bodies, faces, ...) 125 | 126 | // Generate the string for the dataset 127 | List dataset = new List(); 128 | for (int i = 0; i < datasetItems.Count - datasetItems.Count % 4; i += 4) 129 | { 130 | dataset.Add(datasetItems[i] + datasetItems[i + 1] + datasetItems[i + 2] + datasetItems[i + 3]); 131 | } 132 | 133 | // Handle the remainder of items (less than 4) 134 | string temp = ""; 135 | for (int i = datasetItems.Count - datasetItems.Count % 4; i < datasetItems.Count; i++) 136 | { 137 | temp = temp + datasetItems[i]; 138 | } 139 | 140 | if (temp != "" ) 141 | { 142 | dataset.Add(temp); 143 | temp = ""; 144 | } 145 | 146 | return dataset.ToArray(); 147 | 148 | } 149 | 150 | 151 | /// 152 | /// Get all all nodes in a group, including the nodes in a mesh should the group contain meshes. 153 | /// 154 | /// The group object to get the nodes from. 155 | /// An sorted list of the node label and the node object. 156 | public static SortedList GetNodesInGroup(CaePart caePart, CaeGroup group) 157 | { 158 | SmartSelectionManager smartSelectionManager = caePart.SmartSelectionMgr; 159 | SortedList allNodes = new SortedList(); 160 | foreach (TaggedObject taggedObject in group.GetEntities()) 161 | { 162 | if (taggedObject is FENode) 163 | { 164 | allNodes.Add(((FENode)taggedObject).Label, (FENode)taggedObject); 165 | } 166 | if (taggedObject is Mesh) 167 | { 168 | Mesh seedsMesh = (Mesh)taggedObject; 169 | RelatedNodeMethod relatedNodeMethodMesh = smartSelectionManager.CreateNewRelatedNodeMethodFromMesh(seedsMesh, false, false); 170 | foreach (FENode node in relatedNodeMethodMesh.GetNodes()) 171 | { 172 | // meshes share nodes. cannot add the same node twice 173 | try 174 | { 175 | allNodes.Add(node.Label, node); 176 | } 177 | catch (System.Exception) 178 | { 179 | continue; 180 | } 181 | } 182 | } 183 | } 184 | 185 | return allNodes; 186 | } 187 | 188 | 189 | /// 190 | /// Get all all nodes in a group, including the nodes in a mesh should the group contain meshes. 191 | /// 192 | /// The group object to get the nodes from. 193 | /// An sorted list of the node label and the node object. 194 | public static SortedList GetElementsInGroup(CaePart caePart, CaeGroup group) 195 | { 196 | SmartSelectionManager smartSelectionManager = caePart.SmartSelectionMgr; 197 | SortedList allElements = new SortedList(); 198 | foreach (TaggedObject taggedObject in group.GetEntities()) 199 | { 200 | if (taggedObject is FEElement) 201 | { 202 | allElements.Add(((FEElement)taggedObject).Label, (FEElement)taggedObject); 203 | } 204 | if (taggedObject is Mesh) 205 | { 206 | Mesh seedsMesh = (Mesh)taggedObject; 207 | RelatedElemMethod relatedElemMethodMesh = smartSelectionManager.CreateRelatedElemMethod(seedsMesh, false); 208 | foreach (FEElement element in relatedElemMethodMesh.GetElements()) 209 | { 210 | allElements.Add(element.Label, element); 211 | } 212 | } 213 | } 214 | 215 | return allElements; 216 | } 217 | 218 | 219 | /// 220 | /// Creates a universal file dataset header. 221 | /// 222 | /// The label for the dataset. 223 | /// The name for the dataset. 224 | /// The type of result for the dataset: "Elemental" or "Element-Nodal" 225 | /// The header as a string. 226 | public static string CreateGroupHeader(int datasetLabel, string datasetName, string type) 227 | { 228 | theUFSession.Ui.SetStatus("Creating group header"); 229 | string header = ""; 230 | header = header + String.Format("{0, 6}", "-1") + Environment.NewLine; // every dataset starts with -1 231 | header = header + String.Format("{0, 6}", "2414") + Environment.NewLine; // this is the header for dataset 2414 232 | header = header + String.Format("{0, 10}", datasetLabel) + Environment.NewLine; // record 1 233 | header = header + "LOADCASE_NAME_KEY " + datasetName + Environment.NewLine; // record 2 - analysis dataset name 40A2: using this syntax, SimCenter will set the load case name to "datasetName" 234 | 235 | // record 3 236 | header = header + String.Format("{0, 10}", "2") + Environment.NewLine; // Record 3 - dataset location - data on elements 237 | header = header + "RESULT_NAME_KEY " + datasetName + Environment.NewLine; // record 4 - analysis dataset name 40A2: using this syntax, will set the resulttype to Thickness 238 | header = header + "NONE" + Environment.NewLine; // record 5 - analysis dataset name 40A2: using this syntax, Simcenter will parse it and show in the GUI. 239 | header = header + "EXPRESSION_NAME_KEY " + datasetName + Environment.NewLine; // record 6 - analysis dataset name 40A2: using this syntax, Simcenter will parse it and show in the GUI. 240 | header = header + "Creation time: " + DateTime.UtcNow.ToLongDateString() + "\n"; // record 7 - analysis dataset name 40A2: using this syntax, Simcenter will parse it and show in the GUI. 241 | header = header + "NONE" + Environment.NewLine; // record 8 - analysis dataset name 40A2: using this syntax, Simcenter will parse it and show in the GUI. 242 | header = header + String.Format("{0,10}", "1") + String.Format("{0,10}", "1") + String.Format("{0,10}", "1") + String.Format("{0,10}", "94") + String.Format("{0,10}", "2") + String.Format("{0,10}", "1") + Environment.NewLine; // record 9 243 | header = header + String.Format("{0,10}", "1") + String.Format("{0,10}", "0") + String.Format("{0,10}", datasetLabel) + String.Format("{0,10}", "0") + String.Format("{0,10}", "1") + String.Format("{0,10}", "0") + String.Format("{0,10}", "0") + String.Format("{0,10}", "0") + Environment.NewLine; //record 10: using this syntax for Simcenter to parse it properly 244 | header = header + String.Format("{0,10}", "0") + String.Format("{0,10}", "0") + "\n"; //record 11: using this syntax for Simcenter to parse it properly 245 | header = header + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + Environment.NewLine; // record 12: using this syntax for Simcenter to parse it properly 246 | header = header + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + Environment.NewLine; // record 13: using this syntax for Simcenter to parse it properly 247 | 248 | return header; 249 | } 250 | 251 | 252 | /// 253 | /// This function takes a filename and adds an extension and path of the part if not provided by the user. 254 | /// If the fileName contains an extension, this function leaves it untouched, othwerwise adds the provided extension, which defaults to .unv. 255 | /// If the fileName contains a path, this function leaves it untouched, otherwise adds the path of the BasePart as the path. 256 | /// 257 | /// The filename with or without path and .unv extension. 258 | /// Optional: The extension to add if missing. Defaults to .unv. 259 | /// A string with extension and path of basePart if the fileName parameter did not include a path. 260 | public static string CreateFullPath(string fileName, string extension = ".unv") 261 | { 262 | // check if .unv is included in fileName 263 | if (Path.GetExtension(fileName).Length == 0) 264 | { 265 | fileName = fileName + extension; 266 | } 267 | 268 | // check if path is included in fileName, if not add path of the .sim file 269 | string unvFilePath = Path.GetDirectoryName(fileName); 270 | if (unvFilePath == "") 271 | { 272 | // if the basePart file has never been saved, the next will give an error 273 | fileName = Path.Combine(Path.GetDirectoryName(basePart.FullPath), fileName); 274 | } 275 | 276 | return fileName; 277 | } 278 | 279 | } 280 | } -------------------------------------------------------------------------------- /PostProcessing/ExportShellOrientationAsUnv.cs: -------------------------------------------------------------------------------- 1 | // SimCenter support for universal file: 2 | // NX12 https://docs.sw.siemens.com/en-US/product/289054037/doc/PL20190719090640300.advanced/html/xid1404617 3 | // Release SC2019.1 https://docs.sw.siemens.com/en-US/product/289054037/doc/PL20190702084816205.advanced/html/xid1404617 4 | // Release SC2020.1 https://docs.sw.siemens.com/en-US/product/289054037/doc/PL20191009145841552.advanced/html/xid1404617 5 | // Release SC2021.1 https://docs.sw.siemens.com/en-US/product/289054037/doc/PL20200601120302950.advanced/html/xid1404617 6 | // Release SC2022.1 https://docs.sw.siemens.com/en-US/product/289054037/doc/PL20201105151514625.advanced/html/xid1404617 7 | 8 | // Fortran format codes 9 | // https://www.l3harrisgeospatial.com/docs/format_codes_fortran.html 10 | // https://help.perforce.com/pv-wave/2017.1/PVWAVE_Online_Help/pvwave.html#page/Foundation/ap.a.format.066.09.html 11 | 12 | namespace TheScriptingEngineer 13 | { 14 | using System; 15 | using System.IO; // for path operations 16 | using System.Collections.Generic; // for using list 17 | using NXOpen; 18 | using NXOpen.CAE; // so we don't need to start everything with NXOpen.CAE 19 | using NXOpen.Utilities; 20 | using NXOpen.UF; 21 | using NXOpenUI; 22 | using NXOpen.VectorArithmetic; 23 | 24 | public class ExportOrientationThicknessAsUnv 25 | { 26 | static NXOpen.Session theSession = NXOpen.Session.GetSession(); 27 | static NXOpen.UF.UFSession theUFSession = UFSession.GetUFSession(); 28 | static ListingWindow theLW = theSession.ListingWindow; 29 | static BasePart basePart = theSession.Parts.BaseWork; 30 | 31 | public static void Main(string[] args) 32 | { 33 | theLW.Open(); 34 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 35 | 36 | BaseFemPart baseFemPart; 37 | if (basePart as SimPart != null) 38 | { 39 | theLW.WriteFullline("Starting from sim file."); 40 | SimPart simPart = (SimPart)basePart; 41 | baseFemPart = (BaseFemPart)simPart.FemPart; 42 | 43 | // if the baseFemPart is an AssyFemPart then need to make it work for the code to run. 44 | theSession.Parts.SetWork(baseFemPart); 45 | } 46 | else if (basePart as BaseFemPart != null) 47 | { 48 | theLW.WriteFullline("Starting from fem or afem file."); 49 | baseFemPart = (BaseFemPart)basePart; 50 | } 51 | else 52 | { 53 | theLW.WriteFullline("This function needs to start from a .sim, .afem or .fem."); 54 | return; 55 | } 56 | 57 | WriteOrientation(baseFemPart, "Orientation.unv"); 58 | 59 | // return to original work part. 60 | theSession.Parts.SetWork(basePart); 61 | } 62 | 63 | /// 64 | /// This function writes an elemental and element-nodal result to a universal file. 65 | /// The content of the result is the shell thickness of all shell elements in the model. 66 | /// 67 | /// The BaseFemPart to generate the thickness result for. 68 | /// The name of the universal file to write the results to 69 | /// True divides thickness value by 1000, false leaves thickness value in SC3D units 70 | public static void WriteOrientation(BaseFemPart baseFemPart, string fileName, bool sIUnits = true) 71 | { 72 | string[] dataset = CreateOrientationDataset(baseFemPart); 73 | fileName = CreateFullPath(fileName); 74 | theUFSession.Ui.SetStatus("Writing universal file " + fileName); 75 | 76 | using(StreamWriter writetext = new StreamWriter(fileName)) 77 | { 78 | for (int i = 0; i < dataset.Length; i++) 79 | { 80 | writetext.Write(dataset[i]); 81 | writetext.Write(Environment.NewLine); 82 | } 83 | } 84 | } 85 | 86 | /// 87 | /// This method generates the universal file material orientation dataset. 88 | /// 89 | /// The BaseFemPart to generate the material orientation dataset for. 90 | public static string[] CreateOrientationDataset(BaseFemPart baseFemPart) 91 | { 92 | // version 2312.4001 gives a memory access violation error 93 | // 2212 and 94 | if (theSession.FullReleaseNumber == "2312.4001") 95 | { 96 | theLW.WriteFullline("ERROR - Simcenter " + theSession.FullReleaseNumber + " results in a memory access violation when requesting material orientation. Please change to another version."); 97 | theLW.WriteFullline("Simcenter versions 2212 and 2306 do not have this issue. Later versions were not available at the time of writing this script."); 98 | return new string[0]; 99 | } 100 | if (theSession.FullReleaseNumber.Contains("2312")) 101 | { 102 | theLW.WriteFullline("Warning: Memory access violation might occur in Simcenter " + theSession.FullReleaseNumber + " when requesting material orientation. Please change to another version if this happens."); 103 | } 104 | 105 | 106 | theUFSession.Ui.SetStatus("Querying material orientation for all shell elements in the model"); 107 | // get the material orientation for all shell and beam elements 108 | List allShellElements = GetAllShellElements(baseFemPart); 109 | 110 | NXOpen.CAE.ModelCheckManager modelCheckManager = baseFemPart.ModelCheckMgr; 111 | NXOpen.CAE.ModelCheck.ElementMaterialOrientationCheckBuilder elementMaterialOrientationCheckBuilder = modelCheckManager.CreateElementMaterialOrientationCheckBuilder(); 112 | elementMaterialOrientationCheckBuilder.CheckScopeOption = NXOpen.CAE.ModelCheck.CheckScope.Selected; 113 | elementMaterialOrientationCheckBuilder.SetCheckOrientation(NXOpen.CAE.ModelCheck.ElementMaterialOrientationCheckBuilder.MaterialOrientationType.Shell, true); 114 | elementMaterialOrientationCheckBuilder.SetCheckOrientation(NXOpen.CAE.ModelCheck.ElementMaterialOrientationCheckBuilder.MaterialOrientationType.SolidFirstDirection, false); 115 | elementMaterialOrientationCheckBuilder.SetCheckOrientation(NXOpen.CAE.ModelCheck.ElementMaterialOrientationCheckBuilder.MaterialOrientationType.SolidSecondDirection, false); 116 | elementMaterialOrientationCheckBuilder.SetCheckOrientation(NXOpen.CAE.ModelCheck.ElementMaterialOrientationCheckBuilder.MaterialOrientationType.SolidThirdDirection, false); 117 | NXOpen.SelectTaggedObjectList selectTaggedObjectList = elementMaterialOrientationCheckBuilder.SelectionList; 118 | selectTaggedObjectList.Clear(); 119 | selectTaggedObjectList.SetArray(allShellElements.ToArray()); 120 | 121 | NXOpen.CAE.ModelCheck.ElementMaterialOrientationCheckBuilder.MaterialOrientationType[] materialOrientationTypes = new NXOpen.CAE.ModelCheck.ElementMaterialOrientationCheckBuilder.MaterialOrientationType[allShellElements.Count * 2]; 122 | NXOpen.CAE.FEElement[] shellElementsOrientation = new NXOpen.CAE.FEElement[allShellElements.Count * 2]; 123 | // materialOrientationVectors is the material orientation vector for each element in global coordinate system 124 | NXOpen.Vector3d[] materialOrientationVectors = elementMaterialOrientationCheckBuilder.DoCheck(out materialOrientationTypes, out shellElementsOrientation); 125 | 126 | elementMaterialOrientationCheckBuilder.Destroy(); 127 | 128 | // get the in-plane normals for all shell elements 129 | theUFSession.Ui.SetStatus("Calculating in-plane normals for all shell elements in the model"); 130 | NXOpen.Vector3d[] inPlaneNormals = GetShellInPlaneNormal(shellElementsOrientation, materialOrientationVectors); 131 | 132 | // generate the dataset 133 | // don't concatenate strings in a loop, use a string array and join them at the end 134 | // that is much faster 135 | // theUFSession.Ui.SetStatus("Generating material orientation dataset"); 136 | // string[] materialOrientationDataSet = new string[shellElementsOrientation.Length * 5 + 2]; 137 | // materialOrientationDataSet[0] = String.Format("{0, 6}", "-1"); // every dataset starts with -1 138 | // materialOrientationDataSet[1] = String.Format("{0, 6}", "2438"); // this is the header for dataset 2438 139 | 140 | // int descriptorId = -1; 141 | // for (int i = 0; i < shellElementsOrientation.Length; i++) 142 | // { 143 | // if (shellElementsOrientation[i].Shape == NXOpen.CAE.ElementTypes.Shape.Quad) { descriptorId = 94; } 144 | // else if (shellElementsOrientation[i].Shape == NXOpen.CAE.ElementTypes.Shape.Tri) { descriptorId = 95; } 145 | // else { descriptorId = -1; theLW.WriteFullline("ERROR: Element " + shellElementsOrientation[i].Label + " is not a quad or tri."); } 146 | 147 | // materialOrientationDataSet[i * 5 + 2] = String.Format("{0,10}", shellElementsOrientation[i].Label.ToString()) + String.Format("{0,10}", descriptorId); 148 | // materialOrientationDataSet[i * 5 + 3] = String.Format("{0,10}", "0") + String.Format("{0,10}", "0"); 149 | // materialOrientationDataSet[i * 5 + 4] = String.Format("{0,15}", materialOrientationVectors[i].X.ToString("#.0000000E+00")) + String.Format("{0,15}", materialOrientationVectors[i].Y.ToString("#.0000000E+00")) + String.Format("{0,15}", materialOrientationVectors[i].Z.ToString("#.0000000E+00")); 150 | // materialOrientationDataSet[i * 5 + 5] = String.Format("{0,15}", "0.0000000E+00"); 151 | // materialOrientationDataSet[i * 5 + 6] = String.Format("{0,15}", "0.0000000E+00"); 152 | // } 153 | 154 | theUFSession.Ui.SetStatus("Generating material orientation dataset"); 155 | string[] materialOrientationDataSet = new string[shellElementsOrientation.Length * 2 + 16]; 156 | int datasetLabel = 1; 157 | string datasetName = "MaterialOrientation"; 158 | materialOrientationDataSet[0] = String.Format("{0, 6}", "-1"); // every dataset starts with -1 159 | materialOrientationDataSet[1] = String.Format("{0, 6}", "2414"); // this is the header for dataset 2414 160 | materialOrientationDataSet[2] = String.Format("{0, 10}", datasetLabel); // record 1 161 | materialOrientationDataSet[3] = "LOADCASE_NAME_KEY " + datasetName; // record 2 - analysis dataset name 40A2: using this syntax, SimCenter will set the load case name to "datasetName" 162 | materialOrientationDataSet[4] = String.Format("{0, 10}", "2"); // Record 3 - dataset location - data on elements 163 | materialOrientationDataSet[5] = "RESULT_NAME_KEY " + datasetName; // record 4 - analysis dataset name 40A2: using this syntax, will set the resulttype to MaterialOrientation 164 | materialOrientationDataSet[6] = "NONE"; // record 5 - analysis dataset name 40A2: using this syntax, Simcenter will parse it and show in the GUI. 165 | materialOrientationDataSet[7] = "EXPRESSION_NAME_KEY " + datasetName; // record 6 - analysis dataset name 40A2: using this syntax, Simcenter will parse it and show in the GUI. 166 | materialOrientationDataSet[8] = "Creation time: " + DateTime.UtcNow.ToString("dd-MMM-yy HH:mm:sszzz"); // record 7 - analysis dataset name 40A2: using this syntax, Simcenter will parse it and show in the GUI. 167 | materialOrientationDataSet[9] = "NONE"; // record 8 - analysis dataset name 40A2: using this syntax, Simcenter will parse it and show in the GUI. 168 | materialOrientationDataSet[10] = String.Format("{0,10}", "1") + String.Format("{0,10}", "1") + String.Format("{0,10}", "3") + String.Format("{0,10}", "96") + String.Format("{0,10}", "2") + String.Format("{0,10}", "6"); // record 9 169 | materialOrientationDataSet[11] = String.Format("{0,10}", "0") + String.Format("{0,10}", "0") + String.Format("{0,10}", datasetLabel) + String.Format("{0,10}", "0") + String.Format("{0,10}", "1") + String.Format("{0,10}", "0") + String.Format("{0,10}", "0") + String.Format("{0,10}", "0"); //record 10: using this syntax for Simcenter to parse it properly 170 | materialOrientationDataSet[12] = String.Format("{0,10}", "0") + String.Format("{0,10}", "0") + String.Format("{0,10}", "0") + String.Format("{0,10}", "0") + String.Format("{0,10}", "0") + String.Format("{0,10}", "0") + String.Format("{0,10}", "0") + String.Format("{0,10}", "0"); //record 10: using this syntax for Simcenter to parse it properly 171 | materialOrientationDataSet[13] = String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00"); // record 12: using this syntax for Simcenter to parse it properly 172 | materialOrientationDataSet[14] = String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00"); // record 13: using this syntax for Simcenter to parse it properly 173 | 174 | for (int i = 0; i < shellElementsOrientation.Length; i++) 175 | { 176 | materialOrientationDataSet[i * 2 + 15] = String.Format("{0,10}", shellElementsOrientation[i].Label.ToString()) + String.Format("{0,10}", "6"); 177 | materialOrientationDataSet[i * 2 + 16] = String.Format("{0,13}", materialOrientationVectors[i].X.ToString("#.00000E+00")) + String.Format("{0,13}", materialOrientationVectors[i].Y.ToString("#.00000E+00")) + String.Format("{0,13}", materialOrientationVectors[i].Z.ToString("#.00000E+00")) + String.Format("{0,13}", inPlaneNormals[i].X.ToString("#.00000E+00")) + String.Format("{0,13}", inPlaneNormals[i].Y.ToString("#.00000E+00")) + String.Format("{0,13}", inPlaneNormals[i].Z.ToString("#.00000E+00")); 178 | } 179 | 180 | materialOrientationDataSet[materialOrientationDataSet.Length - 1] = String.Format("{0, 6}", "-1"); // every dataset ends with -1 181 | 182 | return materialOrientationDataSet; 183 | } 184 | 185 | 186 | /// 187 | /// Get all elements from the model. 188 | /// Note that this is the most performant way to do so. 189 | /// 190 | /// The BaseFemPart to get the elements from. 191 | /// An array of all FEElements in the baseFemPart. 192 | public static SortedList GetAllFEElements(BaseFemPart baseFemPart) 193 | { 194 | theUFSession.Ui.SetStatus("Getting all element information from the SC3D database"); 195 | SortedList allElements = new SortedList(); 196 | 197 | FEElementLabelMap fEElementLabelMap = baseFemPart.BaseFEModel.FeelementLabelMap; 198 | int elementLabel = fEElementLabelMap.AskNextElementLabel(0); 199 | while (elementLabel > 0) 200 | { 201 | allElements.Add(elementLabel, fEElementLabelMap.GetElement(elementLabel)); 202 | elementLabel = fEElementLabelMap.AskNextElementLabel(elementLabel); 203 | } 204 | 205 | return allElements; 206 | } 207 | 208 | /// 209 | /// Get all shell elements from the model. 210 | /// Note that this is the most performant way to do so. 211 | /// 212 | /// The BaseFemPart to get the elements from. 213 | /// An array of all shell (of type quad and tri) FEElements in the baseFemPart. 214 | public static List GetAllShellElements(BaseFemPart baseFemPart) 215 | { 216 | SortedList allElements = GetAllFEElements(baseFemPart); 217 | List allShellElements = new List(allElements.Count); 218 | 219 | int counter = 0; 220 | for (int i = 0; i < allElements.Count; i++) 221 | { 222 | if (allElements.Values[i].Shape.ToString() == "Quad" || allElements.Values[i].Shape.ToString() == "Tri") 223 | { 224 | allShellElements.Add(allElements.Values[i]); 225 | counter++; 226 | } 227 | } 228 | 229 | allShellElements.TrimExcess(); // Trim the capacity of the allShellElements list to match the number of elements 230 | 231 | return allShellElements; 232 | 233 | } 234 | 235 | 236 | /// 237 | /// This function takes a filename and adds an extension and path of the part if not provided by the user. 238 | /// If the fileName contains an extension, this function leaves it untouched, othwerwise adds the provided extension, which defaults to .unv. 239 | /// If the fileName contains a path, this function leaves it untouched, otherwise adds the path of the BasePart as the path. 240 | /// 241 | /// The filename with or without path and .unv extension. 242 | /// Optional: The extension to add if missing. Defaults to .unv. 243 | /// A string with extension and path of basePart if the fileName parameter did not include a path. 244 | public static string CreateFullPath(string fileName, string extension = ".unv") 245 | { 246 | // check if .unv is included in fileName 247 | if (Path.GetExtension(fileName).Length == 0) 248 | { 249 | fileName = fileName + extension; 250 | } 251 | 252 | // check if path is included in fileName, if not add path of the .sim file 253 | string unvFilePath = Path.GetDirectoryName(fileName); 254 | if (unvFilePath == "") 255 | { 256 | // if the basePart file has never been saved, the next will give an error 257 | fileName = Path.Combine(Path.GetDirectoryName(basePart.FullPath), fileName); 258 | } 259 | 260 | return fileName; 261 | } 262 | 263 | /// 264 | /// Calculates the in-plane normals for a given array of shell elements and material orientation vectors. 265 | /// 266 | /// An array of shell elements. 267 | /// An array of material orientation vectors. There are assumed to lay in the plane of the element, but given in global coordinate system. 268 | /// An array of in-plane normals. 269 | public static NXOpen.Vector3d[] GetShellInPlaneNormal(NXOpen.CAE.FEElement[] shellElements, NXOpen.Vector3d[] materialOrientationVectors) 270 | { 271 | NXOpen.Vector3d[] inPlaneNormals = new NXOpen.Vector3d[shellElements.Length]; 272 | 273 | for (int i = 0; i < shellElements.Length; i++) 274 | { 275 | Vector3 matDirection = new Vector3(materialOrientationVectors[i].X, materialOrientationVectors[i].Y, materialOrientationVectors[i].Z); 276 | // check if matDirection is in the plane of the element 277 | // this hurts performance though, so might want to remove it for larger models. 278 | double test = matDirection.Dot(new Vector3(shellElements[i].GetFaceNormal(0).X, shellElements[i].GetFaceNormal(0).Y, shellElements[i].GetFaceNormal(0).Z)); 279 | if (Math.Abs(test) > 1e-6) 280 | { 281 | theLW.WriteFullline("ERROR: Material orientation vector for element " + shellElements[i].Label + " is not in the plane of the element."); 282 | } 283 | Vector3 inPlaneNormal = matDirection.Cross(new Vector3(shellElements[i].GetFaceNormal(0).X, shellElements[i].GetFaceNormal(0).Y, shellElements[i].GetFaceNormal(0).Z)); 284 | inPlaneNormals[i] = new Vector3d(inPlaneNormal.x, inPlaneNormal.y, inPlaneNormal.z); 285 | } 286 | 287 | return inPlaneNormals; 288 | } 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /PostProcessing/ExportShellThicknessAsUnv.cs: -------------------------------------------------------------------------------- 1 | // SimCenter support for universal file: 2 | // NX12 https://docs.sw.siemens.com/en-US/product/289054037/doc/PL20190719090640300.advanced/html/xid1404617 3 | // Release SC2019.1 https://docs.sw.siemens.com/en-US/product/289054037/doc/PL20190702084816205.advanced/html/xid1404617 4 | // Release SC2020.1 https://docs.sw.siemens.com/en-US/product/289054037/doc/PL20191009145841552.advanced/html/xid1404617 5 | // Release SC2021.1 https://docs.sw.siemens.com/en-US/product/289054037/doc/PL20200601120302950.advanced/html/xid1404617 6 | // Release SC2022.1 https://docs.sw.siemens.com/en-US/product/289054037/doc/PL20201105151514625.advanced/html/xid1404617 7 | 8 | // Fortran format codes 9 | // https://www.l3harrisgeospatial.com/docs/format_codes_fortran.html 10 | // https://help.perforce.com/pv-wave/2017.1/PVWAVE_Online_Help/pvwave.html#page/Foundation/ap.a.format.066.09.html 11 | 12 | namespace TheScriptingEngineer 13 | { 14 | using System; 15 | using System.IO; // for path operations 16 | using System.Collections.Generic; // for using list 17 | using NXOpen; 18 | using NXOpen.CAE; // so we don't need to start everything with NXOpen.CAE 19 | using NXOpen.Utilities; 20 | using NXOpen.UF; 21 | using NXOpenUI; 22 | 23 | public class ExportShellThicknessAsUnv 24 | { 25 | static NXOpen.Session theSession = NXOpen.Session.GetSession(); 26 | static NXOpen.UF.UFSession theUFSession = UFSession.GetUFSession(); 27 | static ListingWindow theLW = theSession.ListingWindow; 28 | static BasePart basePart = theSession.Parts.BaseWork; 29 | 30 | public static void Main(string[] args) 31 | { 32 | theLW.Open(); 33 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 34 | 35 | BaseFemPart baseFemPart; 36 | if (basePart as SimPart != null) 37 | { 38 | theLW.WriteFullline("Starting from sim file."); 39 | SimPart simPart = (SimPart)basePart; 40 | baseFemPart = (BaseFemPart)simPart.FemPart; 41 | 42 | // if the baseFemPart is an AssyFemPart then need to make it work for the code to run. 43 | theSession.Parts.SetWork(baseFemPart); 44 | } 45 | else if (basePart as BaseFemPart != null) 46 | { 47 | theLW.WriteFullline("Starting from fem or afem file."); 48 | baseFemPart = (BaseFemPart)basePart; 49 | } 50 | else 51 | { 52 | theLW.WriteFullline("This function needs to start from a .sim, .afem or .fem."); 53 | return; 54 | } 55 | 56 | bool sIUnits = false; 57 | if (!theSession.IsBatch) 58 | { 59 | string inputString = NXOpenUI.NXInputBox.GetInputString("Export in SI units? (yes or no)", "Please select units", "yes"); 60 | if (inputString == "") 61 | { 62 | // user pressed cancel 63 | return; 64 | } 65 | else if (inputString.Trim().ToLower() == "yes") 66 | { 67 | sIUnits = true; 68 | } 69 | else if (inputString.Trim().ToLower() == "no") 70 | { 71 | sIUnits = false; 72 | } 73 | else 74 | { 75 | UI theUI = NXOpen.UI.GetUI(); 76 | theUI.NXMessageBox.Show("Export shell thickness as universal file", NXMessageBox.DialogType.Error, "Please type yes or no"); 77 | return; 78 | } 79 | 80 | } 81 | 82 | WriteThicknessResults(baseFemPart, "Thickness.unv", sIUnits); 83 | 84 | // return to original work part. 85 | theSession.Parts.SetWork(basePart); 86 | } 87 | 88 | /// 89 | /// This function writes an elemental and element-nodal result to a universal file. 90 | /// The content of the result is the shell thickness of all shell elements in the model. 91 | /// 92 | /// The BaseFemPart to generate the thickness result for. 93 | /// The name of the universal file to write the results to 94 | /// True divides thickness value by 1000, false leaves thickness value in SC3D units 95 | public static void WriteThicknessResults(BaseFemPart baseFemPart, string fileName, bool sIUnits = true) 96 | { 97 | string[][] datasets = CreateThicknessDatasets(baseFemPart, sIUnits); 98 | fileName = CreateFullPath(fileName); 99 | 100 | theUFSession.Ui.SetStatus("Writing universal file"); 101 | using(StreamWriter writetext = new StreamWriter(fileName)) 102 | { 103 | for (int i = 0; i < datasets.Length; i++) 104 | { 105 | for (int j = 0; j < datasets[i].Length; j++) 106 | { 107 | writetext.Write(datasets[i][j]); 108 | } 109 | 110 | writetext.Write(Environment.NewLine); 111 | } 112 | } 113 | } 114 | 115 | /// 116 | /// This method generates the universal file thickness datasets as both elemental and 117 | /// element-nodal result. SI units can be specified. 118 | /// 119 | /// The BaseFemPart to generate the thickness result for. 120 | /// True divides thickness value by 1000, false leaves thickness value in SC3D units 121 | public static string[][] CreateThicknessDatasets(BaseFemPart baseFemPart, bool sIUnits) 122 | { 123 | theLW.WriteFullline("---------- WARNING ----------"); 124 | theLW.WriteFullline("The Element-Nodal result Record 14 field 2 is set to 2: "); 125 | theLW.WriteFullline("'Data present for only first node, all other nodes the same'"); 126 | theLW.WriteFullline("While all nodes are listed individually in Record 15, which is contradictory."); 127 | theLW.WriteFullline("When using externally, update Record 14 field 2 to 1!"); 128 | theLW.WriteFullline("-------- END WARNING ---------"); 129 | 130 | SortedList allElements = GetAllFEElements(baseFemPart); 131 | 132 | string[] thicknessDatasetElemental = new string[allElements.Count + 2]; 133 | string[] thicknessDatasetElementNodal = new string[allElements.Count + 2]; 134 | thicknessDatasetElemental[0] = CreateThicknessHeader(1, "Thickness", "Elemental"); 135 | thicknessDatasetElementNodal[0] = CreateThicknessHeader(1, "Thickness", "Element-Nodal"); // by providing the same label, both results will get grouped under the loadcase thickness 136 | 137 | // foreach (KeyValuePair item in allElements) 138 | for (int i = 0; i < allElements.Count; i++) 139 | { 140 | if (allElements.Values[i].Shape.ToString() == "Quad" || allElements.Values[i].Shape.ToString() == "Tri") 141 | { 142 | thicknessDatasetElemental[i + 1] = CreateThicknessRecords(allElements.Values[i], sIUnits)[0]; 143 | thicknessDatasetElementNodal[i + 1] = CreateThicknessRecords(allElements.Values[i], sIUnits)[1]; 144 | } 145 | } 146 | 147 | thicknessDatasetElemental[thicknessDatasetElemental.Length - 1] = String.Format("{0, 6}", "-1"); 148 | thicknessDatasetElementNodal[thicknessDatasetElementNodal.Length - 1] = String.Format("{0, 6}", "-1"); 149 | 150 | string[][] thicknessDataSets = { thicknessDatasetElemental, thicknessDatasetElementNodal }; 151 | return thicknessDataSets; 152 | } 153 | 154 | /// 155 | /// This function generates result records where the result is a shell element thickness. 156 | /// 157 | /// The FEElement to generate the thickness datasets for. 158 | /// True divides thickness value by 1000, false leaves thickness value in SC3D units 159 | /// An array with the elemental and element-nodal record for the given FEElement. 160 | public static string[] CreateThicknessRecords(FEElement fEElement, bool sIUnits) 161 | { 162 | // passing elementAssociatedDataUtils object for performance, so it does not need be be created for each element. 163 | 164 | // user feedback, but not for all, otherwise some performance hit. 165 | if (fEElement.Label % 1000 == 0) 166 | { 167 | theUFSession.Ui.SetStatus("Generating records for element " + fEElement.Label.ToString()); 168 | } 169 | 170 | double thickness = -1; 171 | Unit thicknessUnit; 172 | fEElement.Mesh.MeshCollector.ElementPropertyTable.GetNamedPropertyTablePropertyValue("Shell Property").PropertyTable.GetScalarWithDataPropertyValue("element thickness", out thickness, out thicknessUnit); 173 | 174 | if (sIUnits) {thickness = thickness / 1000;} 175 | 176 | string Record14Elemental = String.Format("{0, 10}", fEElement.Label) + String.Format("{0, 10}", "1") + Environment.NewLine; 177 | string Record15Elemental = String.Format("{0, 13}", thickness) + Environment.NewLine; 178 | 179 | // even though the second record is 1 (data present for all nodes) SimCenter will not read it properly use the first value for all nodes!! 180 | // some versions of (NX12) will even give a "result file in wrong format" error. In this case, simply change the value to 2 181 | string Record14ElementNodal = String.Format("{0, 10}", fEElement.Label) + String.Format("{0, 10}", "2") + String.Format("{0, 10}", fEElement.GetNodes().Length) + String.Format("{0, 10}", "1") + Environment.NewLine; 182 | 183 | string Record15ElementNodal = ""; 184 | Record15ElementNodal = Record15ElementNodal + String.Format("{0, 13}", String.Format("{0:#.#####E+00}", thickness)); 185 | 186 | Record15ElementNodal = Record15ElementNodal + Environment.NewLine; 187 | 188 | string[] result = { Record14Elemental + Record15Elemental, Record14ElementNodal + Record15ElementNodal }; 189 | return result; 190 | } 191 | 192 | /// 193 | /// Get all elements from the model. 194 | /// Note that this is the most performant way to do so. 195 | /// 196 | /// The BaseFemPart to get the elements from. 197 | /// An array of all FEElements in the baseFemPart. 198 | public static SortedList GetAllFEElements(BaseFemPart baseFemPart) 199 | { 200 | theUFSession.Ui.SetStatus("Getting all element information from the SC3D database"); 201 | SortedList allElements = new SortedList(); 202 | 203 | FEElementLabelMap fEElementLabelMap = baseFemPart.BaseFEModel.FeelementLabelMap; 204 | int elementLabel = fEElementLabelMap.AskNextElementLabel(0); 205 | while (elementLabel > 0) 206 | { 207 | allElements.Add(elementLabel, fEElementLabelMap.GetElement(elementLabel)); 208 | elementLabel = fEElementLabelMap.AskNextElementLabel(elementLabel); 209 | } 210 | 211 | return allElements; 212 | } 213 | 214 | /// 215 | /// Creates a universal file dataset header. 216 | /// 217 | /// The label for the dataset. 218 | /// The name for the dataset. 219 | /// The type of result for the dataset: "Elemental" or "Element-Nodal" 220 | /// The header as a string. 221 | public static string CreateThicknessHeader(int datasetLabel, string datasetName, string type) 222 | { 223 | string header = ""; 224 | header = header + String.Format("{0, 6}", "-1") + Environment.NewLine; // every dataset starts with -1 225 | header = header + String.Format("{0, 6}", "2414") + Environment.NewLine; // this is the header for dataset 2414 226 | header = header + String.Format("{0, 10}", datasetLabel) + Environment.NewLine; // record 1 227 | header = header + "LOADCASE_NAME_KEY " + datasetName + Environment.NewLine; // record 2 - analysis dataset name 40A2: using this syntax, SimCenter will set the load case name to "datasetName" 228 | 229 | // record 3 230 | if (type.ToLower().Trim() == "elemental") 231 | { 232 | header = header + String.Format("{0, 10}", "2") + Environment.NewLine; // Record 3 - dataset location - data on elements 233 | } 234 | else if (type.ToLower().Trim() == "element-nodal" || type.ToLower().Trim() == "elementnodal" || type.ToLower().Trim() == "element nodal") 235 | { 236 | header = header + String.Format("{0, 10}", "3") + Environment.NewLine; // Record 3 - dataset location - data at nodes on element 237 | } 238 | else 239 | { 240 | theLW.WriteFullline("Unsupported type " + type + " in CreateThicknessHeader. Should be \"elemental\" or \"element-nodal\""); 241 | return null; 242 | } 243 | 244 | header = header + "RESULT_NAME_KEY " + datasetName + Environment.NewLine; // record 4 - analysis dataset name 40A2: using this syntax, will set the resulttype to Thickness 245 | header = header + "NONE" + Environment.NewLine; // record 5 - analysis dataset name 40A2: using this syntax, Simcenter will parse it and show in the GUI. 246 | header = header + "EXPRESSION_NAME_KEY " + datasetName + Environment.NewLine; // record 6 - analysis dataset name 40A2: using this syntax, Simcenter will parse it and show in the GUI. 247 | header = header + "Creation time: " + DateTime.UtcNow.ToLongDateString() + "\n"; // record 7 - analysis dataset name 40A2: using this syntax, Simcenter will parse it and show in the GUI. 248 | header = header + "NONE" + Environment.NewLine; // record 8 - analysis dataset name 40A2: using this syntax, Simcenter will parse it and show in the GUI. 249 | header = header + String.Format("{0,10}", "1") + String.Format("{0,10}", "1") + String.Format("{0,10}", "1") + String.Format("{0,10}", "94") + String.Format("{0,10}", "2") + String.Format("{0,10}", "1") + Environment.NewLine; // record 9 250 | header = header + String.Format("{0,10}", "1") + String.Format("{0,10}", "0") + String.Format("{0,10}", datasetLabel) + String.Format("{0,10}", "0") + String.Format("{0,10}", "1") + String.Format("{0,10}", "0") + String.Format("{0,10}", "0") + String.Format("{0,10}", "0") + Environment.NewLine; //record 10: using this syntax for Simcenter to parse it properly 251 | header = header + String.Format("{0,10}", "0") + String.Format("{0,10}", "0") + "\n"; //record 11: using this syntax for Simcenter to parse it properly 252 | header = header + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + Environment.NewLine; // record 12: using this syntax for Simcenter to parse it properly 253 | header = header + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + String.Format("{0,13}", "0.00000E+00") + Environment.NewLine; // record 13: using this syntax for Simcenter to parse it properly 254 | 255 | return header; 256 | } 257 | 258 | /// 259 | /// This function takes a filename and adds an extension and path of the part if not provided by the user. 260 | /// If the fileName contains an extension, this function leaves it untouched, othwerwise adds the provided extension, which defaults to .unv. 261 | /// If the fileName contains a path, this function leaves it untouched, otherwise adds the path of the BasePart as the path. 262 | /// 263 | /// The filename with or without path and .unv extension. 264 | /// Optional: The extension to add if missing. Defaults to .unv. 265 | /// A string with extension and path of basePart if the fileName parameter did not include a path. 266 | public static string CreateFullPath(string fileName, string extension = ".unv") 267 | { 268 | // check if .unv is included in fileName 269 | if (Path.GetExtension(fileName).Length == 0) 270 | { 271 | fileName = fileName + extension; 272 | } 273 | 274 | // check if path is included in fileName, if not add path of the .sim file 275 | string unvFilePath = Path.GetDirectoryName(fileName); 276 | if (unvFilePath == "") 277 | { 278 | // if the basePart file has never been saved, the next will give an error 279 | fileName = Path.Combine(Path.GetDirectoryName(basePart.FullPath), fileName); 280 | } 281 | 282 | return fileName; 283 | } 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /PostProcessing/ListNodalResults.cs: -------------------------------------------------------------------------------- 1 | namespace TheScriptingEngineer 2 | { 3 | using System; 4 | using System.IO; // for path operations 5 | using System.Collections.Generic; // for lists 6 | using NXOpen; // so we can use NXOpen functionality 7 | using NXOpen.CAE; // so we don't need to start everything with NXOpen.CAE 8 | using NXOpenUI; 9 | using NXOpen.UF; 10 | using NXOpen.Utilities; 11 | 12 | public class ListReactionForces 13 | { 14 | // global variables used throughout 15 | public static Session theSession = Session.GetSession(); 16 | public static ListingWindow theLW = theSession.ListingWindow; 17 | public static BasePart basePart = theSession.Parts.BaseWork; 18 | 19 | public static void Main(string[] args) 20 | { 21 | // entrypoint for NX 22 | theLW.Open(); 23 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 24 | 25 | int nodeLabel = 2289198; 26 | ListNodalValues("SolutionName", 1, 1, "Reaction Moment - Nodal", nodeLabel); 27 | 28 | } 29 | 30 | /// 31 | /// Retrieves and prints nodal values for a specified node label. 32 | /// Currently hard coded for a nodal result with X,Y,Z and Magnitude as components 33 | /// 34 | /// Name of the solution. 35 | /// Subcase number. 36 | /// Iteration number. 37 | /// Type of result. 38 | /// Label of the node. 39 | public static void ListNodalValues(string solutionName, int subcase, int iteration, string resultType, int nodeLabel) 40 | { 41 | PostInput postInput = new PostInput(solutionName, subcase, iteration, resultType); // Note that the user starts counting at 1! 42 | PostInput[] postInputArray = new PostInput[] { postInput }; 43 | SolutionResult[] solutionResults = LoadResults(postInputArray); 44 | Result result = (Result)solutionResults[0]; 45 | ResultType[] resultTypes = GetResultTypes(postInputArray, solutionResults); 46 | ResultParameters[] resultParameters = GetResultParamaters(resultTypes, Result.ShellSection.Maximum, Result.Component.Magnitude, false); 47 | ResultAccess resultAccess = theSession.ResultManager.CreateResultAccess(result, resultParameters[0]); 48 | resultAccess.AskNodalResultAllComponents(solutionResults[0].AskNodeIndex(nodeLabel), out double[] nodalData); 49 | // printing is hard coded as X Y Z Magnitude 50 | theLW.WriteFullline("X:\t" + nodalData[0].ToString() + "\tY:\t" + nodalData[1].ToString() + "\tZ:\t" + nodalData[2].ToString() + "\tMagnitude:\t" + nodalData[3].ToString()); 51 | } 52 | 53 | /// 54 | /// Retrieves and prints Element-Nodal values for a specified element label. 55 | /// Currently hard coded for Beam resultants forces with 2 nodes and 6 components per node. 56 | /// 57 | /// Name of the solution. 58 | /// Subcase number. 59 | /// Iteration number. 60 | /// Type of result. 61 | /// Label of the element to list the Element-Nodal results for. 62 | public static void ListElementNodalValues(string solutionName, int subcase, int iteration, string resultType, int elementLabel) 63 | { 64 | PostInput postInput = new PostInput(solutionName, subcase, iteration, resultType); // Note that the user starts counting at 1! 65 | PostInput[] postInputArray = new PostInput[] { postInput }; 66 | SolutionResult[] solutionResults = LoadResults(postInputArray); 67 | Result result = (Result)solutionResults[0]; 68 | ResultType[] resultTypes = GetResultTypes(postInputArray, solutionResults); 69 | ResultParameters[] resultParameters = GetResultParamaters(resultTypes, Result.ShellSection.Maximum, Result.Component.Axial, false); 70 | ResultAccess resultAccess = theSession.ResultManager.CreateResultAccess(result, resultParameters[0]); 71 | resultAccess.AskElementNodalResultAllComponents(solutionResults[0].AskElementIndex(elementLabel), out int[] nodeIndex, out int numComponents, out double[] elementNodalData); 72 | // for (int i = 0; i < nodeIndex.Length; i++) 73 | // { 74 | // theLW.WriteFullline("Results for node " + solutionResults[0].AskNodeLabel(nodeIndex[i])); 75 | // for (int j = 0; j < numComponents; j++) 76 | // { 77 | // theLW.WriteFullline("Value: " + elementNodalData[i * numComponents + j]); 78 | // } 79 | // } 80 | 81 | // printing is hard coded as beam resultant forces 82 | // String.Format("{0, 13}", String.Format("{0:#.#####E+00}", elementNodalData[0]) for scientific notation, all with 13 length 83 | // .PadRight(13) for left align (note the 13 in both) 84 | theLW.WriteFullline(String.Format("{0, 13}", "ID:".PadRight(13)) + String.Format("{0, 13}", "Nxx:".PadRight(13)) + String.Format("{0, 13}", "Myy:".PadRight(13)) + String.Format("{0, 13}", "Mzz:".PadRight(13)) +String.Format("{0, 13}", "Mxx:".PadRight(13)) + String.Format("{0, 13}", "Qxy:".PadRight(13)) + String.Format("{0, 13}", "Qxz:".PadRight(13))); 85 | theLW.WriteFullline(String.Format("{0, 13}", solutionResults[0].AskNodeLabel(nodeIndex[0]).ToString().PadRight(13)) + 86 | String.Format("{0, 13}", String.Format("{0:#.#####E+00}", elementNodalData[0]).PadRight(13)) + 87 | String.Format("{0, 13}", String.Format("{0:#.#####E+00}", elementNodalData[1]).PadRight(13)) + 88 | String.Format("{0, 13}", String.Format("{0:#.#####E+00}", elementNodalData[2]).PadRight(13)) + 89 | String.Format("{0, 13}", String.Format("{0:#.#####E+00}", elementNodalData[3]).PadRight(13)) + 90 | String.Format("{0, 13}", String.Format("{0:#.#####E+00}", elementNodalData[4]).PadRight(13)) + 91 | String.Format("{0, 13}", String.Format("{0:#.#####E+00}", elementNodalData[5]).PadRight(13))); 92 | theLW.WriteFullline(String.Format("{0, 13}", solutionResults[0].AskNodeLabel(nodeIndex[1]).ToString().PadRight(13)) + 93 | String.Format("{0, 13}", String.Format("{0:#.#####E+00}", elementNodalData[6]).PadRight(13)) + 94 | String.Format("{0, 13}", String.Format("{0:#.#####E+00}", elementNodalData[7]).PadRight(13)) + 95 | String.Format("{0, 13}", String.Format("{0:#.#####E+00}", elementNodalData[8]).PadRight(13)) + 96 | String.Format("{0, 13}", String.Format("{0:#.#####E+00}", elementNodalData[9]).PadRight(13)) + 97 | String.Format("{0, 13}", String.Format("{0:#.#####E+00}", elementNodalData[10]).PadRight(13)) + 98 | String.Format("{0, 13}", String.Format("{0:#.#####E+00}", elementNodalData[11]).PadRight(13))); 99 | } 100 | 101 | 102 | /// 103 | /// Helper function for EnvelopeResults. 104 | /// Returns an array of resultparameters with the given parameters for each ResultType. 105 | /// 106 | /// The array of ResultType used in the envelope 107 | /// The component for which to perform the envelope operation. 108 | /// The section to use in the envelope operation. 109 | /// envelope using absolute values or signed values. Note that you sort the absolute values and not take the absolute value of the sorted result. 110 | /// Returns an array of ResultParameters. 111 | public static ResultParameters[] GetResultParamaters(BaseResultType[] resultTypes, Result.ShellSection resultShellSection, Result.Component resultComponent, bool absolute) 112 | { 113 | ResultParameters[] resultParametersArray = new ResultParameters[resultTypes.Length]; 114 | 115 | for (int i = 0; i < resultTypes.Length; i++) 116 | { 117 | ResultParameters resultParameters = theSession.ResultManager.CreateResultParameters(); 118 | resultParameters.SetGenericResultType(resultTypes[i]); 119 | resultParameters.SetShellSection(resultShellSection); 120 | resultParameters.SetResultComponent(resultComponent); 121 | resultParameters.SetCoordinateSystem(Result.CoordinateSystem.AbsoluteRectangular); 122 | resultParameters.SetSelectedCoordinateSystem(Result.CoordinateSystemSource.None, -1); 123 | resultParameters.MakeElementResult(false); 124 | 125 | Result.Component[] components; 126 | resultTypes[i].AskComponents(out components); 127 | Unit unit = resultTypes[i].AskDefaultUnitForComponent(components[0]); 128 | resultParameters.SetUnit(unit); 129 | 130 | resultParameters.SetAbsoluteValue(absolute); 131 | resultParameters.SetTensorComponentAbsoluteValue(Result.TensorDerivedAbsolute.DerivedComponent); 132 | 133 | resultParametersArray[i] = resultParameters; 134 | } 135 | 136 | return resultParametersArray; 137 | } 138 | 139 | 140 | /// 141 | /// This function returns the SimSolution object with the given name. 142 | /// 143 | /// The name of the solution to return. Case insensitive. 144 | /// The SimSolution object if found, Null otherwise. 145 | public static SimSolution GetSolution(string SolutionName) 146 | { 147 | SimPart simPart = (SimPart)basePart; 148 | SimSolution[] simSolutions = simPart.Simulation.Solutions.ToArray(); 149 | SimSolution simSolution = Array.Find(simSolutions, solution => solution.Name.ToLower() == SolutionName.ToLower()); 150 | return simSolution; 151 | } 152 | 153 | /// 154 | /// Helper function for CombineResults, ExportResult and SortResults. 155 | /// Loads the results in the provided array of PostInput 156 | /// 157 | /// The result of each of the provided solutions is loaded. 158 | /// The type of SimResultReference eg. Structural 159 | /// Returns an array of SolutionResult. 160 | public static SolutionResult[] LoadResults(PostInput[] postInputs, string referenceType = "Structural") 161 | { 162 | SolutionResult[] solutionResults = new SolutionResult[postInputs.Length]; 163 | SimPart simPart = (SimPart)basePart; 164 | SimSimulation simSimulation = (SimSimulation)simPart.Simulation; 165 | 166 | for (int i = 0; i < postInputs.Length; i++) 167 | { 168 | SimSolution simSolution = GetSolution(postInputs[i].Solution); 169 | SimResultReference simResultReference = (SimResultReference)simSolution.Find(referenceType); 170 | //SimResultReference simResultReference = simSolution.GetResultReferenceByIndex(0); // for structural 171 | 172 | try 173 | { 174 | // SolutionResult[filename_solutionname] 175 | solutionResults[i] = (SolutionResult)theSession.ResultManager.FindObject("SolutionResult[" + System.IO.Path.GetFileName(simPart.FullPath) + "_" + simSolution.Name + "]"); 176 | } 177 | catch (System.Exception) 178 | { 179 | solutionResults[i] = theSession.ResultManager.CreateReferenceResult(simResultReference); 180 | } 181 | } 182 | 183 | return solutionResults; 184 | } 185 | 186 | /// 187 | /// Helper function for CombineResults and GetResultParameters. 188 | /// Returns the ResultTypes specified in PostInputs 189 | /// 190 | /// The input as an array of PostInput. 191 | /// The already loaded results to search through for the results. 192 | /// Returns the result objects. 193 | public static ResultType[] GetResultTypes(PostInput[] postInputs, SolutionResult[] solutionResults) 194 | { 195 | ResultType[] resultTypes = new ResultType[postInputs.Length]; 196 | 197 | for (int i = 0; i < postInputs.Length; i++) 198 | { 199 | BaseLoadcase[] baseLoadcases = solutionResults[i].GetLoadcases(); 200 | Loadcase loadcase = (Loadcase)baseLoadcases[postInputs[i].Subcase - 1]; // user starts counting at 1 201 | BaseIteration[] baseIterations = loadcase.GetIterations(); 202 | Iteration iteration = (Iteration)baseIterations[postInputs[i].Iteration - 1]; // user starts counting at 1 203 | BaseResultType[] baseResultTypes = iteration.GetResultTypes(); 204 | ResultType resultType = (ResultType)Array.Find(baseResultTypes, type => type.Name.ToLower().Trim() == postInputs[i].ResultType.ToLower().Trim()); 205 | resultTypes[i] = resultType; 206 | } 207 | 208 | return resultTypes; 209 | } 210 | } 211 | 212 | /// 213 | /// Class for defining results in postprocessing 214 | /// For all selections, the user starts counting at 1!. 215 | /// 216 | public class PostInput 217 | { 218 | /// 219 | /// The solution to which the loadcase and iteration belong to. 220 | /// 221 | public string Solution { get; set; } 222 | 223 | /// 224 | /// The loadcase to which the iteration belongs to. 225 | /// 226 | public int Subcase { get; set; } 227 | 228 | /// 229 | /// The iteration, defaults to 0 for linear results. 230 | /// 231 | public int Iteration { get; set; } 232 | 233 | /// 234 | /// The result number. This is the index of the result as show in the GUI. 235 | /// 236 | public string ResultType { get; set; } 237 | 238 | /// 239 | /// The identifier for the input as used in the formula. 240 | /// 241 | public string Identifier { get; set; } 242 | 243 | /// 244 | /// Parameterless constructor. 245 | /// 246 | public PostInput() 247 | { 248 | Solution = ""; 249 | Subcase = -1; 250 | Iteration = -1; 251 | ResultType = ""; 252 | Identifier = ""; 253 | } 254 | 255 | /// 256 | /// Constructor 257 | /// 258 | public PostInput(string solution, int subcase, int iteration, string resulttype, string identifier) 259 | { 260 | Solution = solution; 261 | Subcase = subcase; 262 | Iteration = iteration; 263 | ResultType = resulttype; 264 | Identifier = identifier; 265 | } 266 | 267 | /// 268 | /// Constructor 269 | /// 270 | public PostInput(string solution, int subcase, int iteration, string resulttype) 271 | { 272 | Solution = solution; 273 | Subcase = subcase; 274 | Iteration = iteration; 275 | ResultType = resulttype; 276 | Identifier = ""; 277 | } 278 | 279 | /// 280 | /// Returns a string representation of PostInput. 281 | /// 282 | /// The string. 283 | public override string ToString() 284 | { 285 | if (Identifier != "") 286 | { 287 | return "Solution: " + Solution + " Subcase: " + Subcase.ToString() + " Iteration: " + Iteration.ToString() + " ResultType: " + ResultType + " Identifier: " + Identifier; 288 | } 289 | else 290 | { 291 | return "Solution: " + Solution + " Subcase: " + Subcase.ToString() + " Iteration: " + Iteration.ToString() + " ResultType: " + ResultType; 292 | } 293 | } 294 | 295 | /// 296 | /// This function returns all the Identifiers in PostInputs in an array of string 297 | /// 298 | /// The array of PostInput for which to get the Identifiers. 299 | /// The Identifiers in the PostInputs. 300 | public static string[] GetIdentifiers(PostInput[] postInputs) 301 | { 302 | string[] identifiers = new string[postInputs.Length]; 303 | for (int i = 0; i < postInputs.Length; i++) 304 | { 305 | identifiers[i] = postInputs[i].Identifier; 306 | } 307 | 308 | return identifiers; 309 | } 310 | } 311 | } -------------------------------------------------------------------------------- /PreProcessing/AddRelatedNodesAndElements.cs: -------------------------------------------------------------------------------- 1 | namespace TheScriptingEngineer 2 | { 3 | using System; 4 | using System.IO; // for path operations 5 | using System.Collections.Generic; // for lists 6 | using NXOpen; // so we can use NXOpen functionality 7 | using NXOpen.CAE; // so we don't need to start everything with NXOpen.CAE 8 | using NXOpenUI; 9 | using NXOpen.UF; 10 | using NXOpen.Utilities; 11 | using NXOpen.VectorArithmetic; 12 | 13 | public class RelatedNodesAndElements 14 | { 15 | // global variables used throughout 16 | public static Session theSession = Session.GetSession(); 17 | public static ListingWindow theLW = theSession.ListingWindow; 18 | public static BasePart basePart = theSession.Parts.BaseWork; 19 | 20 | public static void Main(string[] args) 21 | { 22 | theLW.Open(); 23 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 24 | 25 | if (basePart as CaePart == null) 26 | { 27 | theLW.WriteFullline("AddRelatedNodesAndElements needs to start from a CAE part."); 28 | return; 29 | } 30 | 31 | AddRelatedNodesAndElements((CaePart)basePart); 32 | } 33 | 34 | /// 35 | /// This function cycles through all cae groups in a CaePart. 36 | /// For each group it adds the related nodes and elements for the bodies and faces in the group. 37 | /// Practical for repopulating groups after a (partial) remesh. 38 | /// Function is idempotent. 39 | /// 40 | /// The CaePart to perform this operation on. 41 | /// OPTIONAL: Set to true to also add beam elements on the edges of the bodies and faces. 42 | public static void AddRelatedNodesAndElements(CaePart caePart, bool addBeamElements = false) 43 | { 44 | CaeGroup[] caeGroups = caePart.CaeGroups.ToArray(); 45 | foreach (CaeGroup item in caeGroups) 46 | { 47 | theLW.WriteFullline("Processing group " + item.Name); 48 | List seedsBody = new List(); 49 | List seedsFace = new List(); 50 | List seedsEdge = new List(); // for beam elements on the edges 51 | 52 | foreach (TaggedObject taggedObject in item.GetEntities()) 53 | { 54 | if (taggedObject is CAEBody) 55 | { 56 | seedsBody.Add((CAEBody)taggedObject); 57 | if (addBeamElements) 58 | { 59 | seedsEdge.AddRange(GetEdgesFromBody((CAEBody)taggedObject)); 60 | } 61 | } 62 | else if (taggedObject is CAEFace) 63 | { 64 | seedsFace.Add((CAEFace)taggedObject); 65 | if (addBeamElements) 66 | { 67 | seedsEdge.AddRange(GetEdgesFromFace((CAEFace)taggedObject)); 68 | } 69 | } 70 | } 71 | 72 | SmartSelectionManager smartSelectionManager = caePart.SmartSelectionMgr; 73 | 74 | RelatedElemMethod relatedElemMethodBody = smartSelectionManager.CreateRelatedElemMethod(seedsBody.ToArray(), false); 75 | RelatedNodeMethod relatedNodeMethodBody = smartSelectionManager.CreateRelatedNodeMethod(seedsBody.ToArray(), false); 76 | // comment previous line and uncomment next line for NX version 2007 (release 2022.1) and later 77 | // RelatedNodeMethod relatedNodeMethodBody = smartSelectionManager.CreateNewRelatedNodeMethodFromBodies(seedsBody.ToArray(), false, false); 78 | 79 | item.AddEntities(relatedElemMethodBody.GetElements()); 80 | item.AddEntities(relatedNodeMethodBody.GetNodes()); 81 | 82 | RelatedElemMethod relatedElemMethodFace = smartSelectionManager.CreateRelatedElemMethod(seedsFace.ToArray(), false); 83 | RelatedNodeMethod relatedNodeMethodFace = smartSelectionManager.CreateRelatedNodeMethod(seedsFace.ToArray(), false); 84 | // comment previous line and uncomment next line for NX version 2007 (release 2022.1) and later 85 | // RelatedNodeMethod relatedNodeMethodFace = smartSelectionManager.CreateNewRelatedNodeMethodFromFaces(seedsFace.ToArray(), false, false); 86 | 87 | item.AddEntities(relatedElemMethodFace.GetElements()); 88 | item.AddEntities(relatedNodeMethodFace.GetNodes()); 89 | 90 | if (addBeamElements) 91 | { 92 | RelatedElemMethod relatedElemMethodEdge = smartSelectionManager.CreateRelatedElemMethod(GetUniqueElements(seedsEdge).ToArray(), false); 93 | item.AddEntities(relatedElemMethodEdge.GetElements()); 94 | } 95 | } 96 | } 97 | 98 | 99 | public static CAEEdge[] GetEdgesFromBody(CAEBody body) 100 | { 101 | FemPart femPart = (FemPart)basePart; 102 | SmartSelectionManager smartSelectionManager = femPart.SmartSelectionMgr; 103 | RelatedEdgeMethod relatedEdgeMethod = smartSelectionManager.CreateRelatedEdgeMethod(new[] { body }, false); 104 | return relatedEdgeMethod.GetEdges(); 105 | } 106 | 107 | /// 108 | /// Returns all edges of a CAEFace. 109 | /// 110 | /// The face for which to return all edges. 111 | /// An array of CAEEdge with all edges of the face. 112 | public static CAEEdge[] GetEdgesFromFace(CAEFace face) 113 | { 114 | FemPart femPart = (FemPart)basePart; 115 | SmartSelectionManager smartSelectionManager = femPart.SmartSelectionMgr; 116 | RelatedEdgeMethod relatedEdgeMethod = smartSelectionManager.CreateRelatedEdgeMethod(new[] { face }, false); 117 | return relatedEdgeMethod.GetEdges(); 118 | } 119 | 120 | public static List GetUniqueElements(List inputList) 121 | { 122 | List uniques = new List(); 123 | foreach (CAEEdge item in inputList) 124 | { 125 | if (!uniques.Contains(item)) uniques.Add(item); 126 | } 127 | return uniques; 128 | } 129 | } 130 | } -------------------------------------------------------------------------------- /PreProcessing/ChangeMaterial.cs: -------------------------------------------------------------------------------- 1 | namespace TheScriptingEngineer 2 | { 3 | using System; 4 | using System.IO; // for path operations 5 | using System.Collections.Generic; // for lists 6 | using NXOpen; // so we can use NXOpen functionality 7 | using NXOpen.CAE; // so we don't need to start everything with NXOpen.CAE 8 | using NXOpenUI; 9 | using NXOpen.UF; 10 | using NXOpen.Utilities; 11 | 12 | public class ChangeMaterial 13 | { 14 | // global variables used throughout 15 | public static Session theSession = Session.GetSession(); 16 | public static UFSession theUFSession = UFSession.GetUFSession(); 17 | public static ListingWindow theLW = theSession.ListingWindow; 18 | public static BasePart basePart = theSession.Parts.BaseWork; 19 | 20 | public static void Main(string[] args) 21 | { 22 | // entrypoint for NX 23 | theLW.Open(); 24 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 25 | 26 | ReplaceMaterial("name of the new material"); 27 | } 28 | 29 | 30 | /// 31 | /// Replaces the material in all physical property tables of the current base part. 32 | /// 33 | /// The name of the new material to set in the tables. 34 | public static void ReplaceMaterial(string newMaterialName) 35 | { 36 | BaseFemPart baseFemPart = (BaseFemPart)basePart; 37 | foreach (PhysicalPropertyTable table in baseFemPart.PhysicalPropertyTables.ToArray()) 38 | { 39 | theLW.WriteFullline("Updating table " + table.JournalIdentifier); 40 | PhysicalPropertyChangeMaterial(table, newMaterialName); 41 | } 42 | } 43 | 44 | 45 | /// 46 | /// Changes the material of a physical property table to the specified material. 47 | /// Note that the material needs to exist. 48 | /// 49 | /// The physical property table to modify. 50 | /// The name of the new material to set. 51 | public static void PhysicalPropertyChangeMaterial(PhysicalPropertyTable physicalPropertyTable, string newMaterialName) 52 | { 53 | BaseFemPart baseFemPart = (BaseFemPart)basePart; 54 | PropertyTable propertyTable = physicalPropertyTable.PropertyTable; 55 | 56 | PhysicalMaterial[] physicalMaterials = baseFemPart.MaterialManager.PhysicalMaterials.GetUsedMaterials(); 57 | PhysicalMaterial physicalMaterial = Array.Find(physicalMaterials, item => item.Name == newMaterialName); 58 | if (physicalMaterial == null) 59 | { 60 | theLW.WriteFullline("No material could be found with the name " + newMaterialName); 61 | return; 62 | } 63 | propertyTable.SetMaterialPropertyValue("material", false, physicalMaterial); 64 | 65 | NXOpen.Session.UndoMarkId markId= theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, null); 66 | int nErrs = theSession.UpdateManager.DoUpdate(markId); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /PreProcessing/CreateMeshCollector.cs: -------------------------------------------------------------------------------- 1 | namespace TheScriptingEngineer 2 | { 3 | using System; 4 | using System.IO; // for path operations 5 | using System.Collections.Generic; // for lists 6 | using NXOpen; // so we can use NXOpen functionality 7 | using NXOpen.CAE; // so we don't need to start everything with NXOpen.CAE 8 | using NXOpenUI; 9 | using NXOpen.UF; 10 | using NXOpen.Utilities; 11 | 12 | public class CreateMeshCollectorClass 13 | { 14 | // global variables used throughout 15 | static Session theSession = Session.GetSession(); 16 | static ListingWindow theLW = theSession.ListingWindow; 17 | static CaePart caePart = (CaePart)theSession.Parts.BaseWork; 18 | 19 | public static void Main(string[] args) 20 | { 21 | theLW.Open(); 22 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 23 | 24 | double[] thicknesses = {6, 8, 10, 12, 14, 15, 16, 18, 20, 22, 25, 30, 35, 40, 45, 50, 60, 70, 80, 90, 100}; 25 | for (int ii = 0; ii < thicknesses.Length; ii++) 26 | { 27 | CreateMeshCollector(thicknesses[ii]); 28 | } 29 | 30 | //CreateMeshCollector(8, 5); 31 | //CreateMeshCollector(10, 6); 32 | //CreateMeshCollector(12, 7); 33 | //CreateMeshCollector(14, 8); 34 | } 35 | 36 | /// 37 | /// Creates a 2d mesh collector with the given thickness. 38 | /// The color of the mesh collector is set an an integer of 10 times the label. 39 | /// The label is one higher from the label of the last physical property. 40 | /// This implicilty assumes that physical properties are created with ascending labels. 41 | /// 42 | /// The thickness of the 2d mesh. 43 | public static void CreateMeshCollector(double thickness) 44 | { 45 | FemPart femPart = (FemPart)theSession.Parts.BaseWork; 46 | 47 | FEModel fEModel = (FEModel)femPart.BaseFEModel; 48 | MeshManager meshManager = (MeshManager)fEModel.MeshManager; 49 | 50 | MeshCollector nullNXOpen_CAE_MeshCollector = null; 51 | MeshCollectorBuilder meshCollectorBuilder = meshManager.CreateCollectorBuilder(nullNXOpen_CAE_MeshCollector, "ThinShell"); 52 | 53 | // Get the highest label from the physical properties to then pass as parameter in the creation of a physical property. 54 | PhysicalPropertyTable[] physicalPropertyTables = caePart.PhysicalPropertyTables.ToArray(); 55 | int maxLabel = 1; 56 | if (physicalPropertyTables.Length != 0) 57 | { 58 | maxLabel = physicalPropertyTables[physicalPropertyTables.Length - 1].Label + 1; 59 | } 60 | 61 | PhysicalPropertyTable physicalPropertyTable; 62 | physicalPropertyTable = femPart.PhysicalPropertyTables.CreatePhysicalPropertyTable("PSHELL", "NX NASTRAN - Structural", "NX NASTRAN", "PSHELL2", maxLabel); 63 | physicalPropertyTable.SetName(thickness.ToString() + "mm"); 64 | 65 | //NXOpen.PhysicalMaterial physicalMaterial1 = (NXOpen.PhysicalMaterial)workFemPart.MaterialManager.PhysicalMaterials.FindObject("PhysicalMaterial[Steel]"); 66 | PhysicalMaterial[] physicalMaterials = femPart.MaterialManager.PhysicalMaterials.GetUsedMaterials(); 67 | PhysicalMaterial steel = Array.Find(physicalMaterials, material => material.Name == "Steel"); 68 | if (steel == null) 69 | { 70 | steel = femPart.MaterialManager.PhysicalMaterials.LoadFromNxlibrary("Steel"); 71 | } 72 | 73 | PropertyTable propertyTable; 74 | propertyTable = physicalPropertyTable.PropertyTable; 75 | propertyTable.SetMaterialPropertyValue("material", false, steel); 76 | propertyTable.SetTablePropertyWithoutValue("bending material"); 77 | propertyTable.SetTablePropertyWithoutValue("transverse shear material"); 78 | propertyTable.SetTablePropertyWithoutValue("membrane-bending coupling material"); 79 | 80 | Unit unitMilliMeter = (Unit)femPart.UnitCollection.FindObject("MilliMeter"); 81 | propertyTable.SetBaseScalarWithDataPropertyValue("element thickness", thickness.ToString(), unitMilliMeter); 82 | 83 | meshCollectorBuilder.CollectorName = thickness.ToString() + "mm"; //"8mm"; 84 | meshCollectorBuilder.PropertyTable.SetNamedPropertyTablePropertyValue("Shell Property", physicalPropertyTable); 85 | 86 | NXObject nXObject = meshCollectorBuilder.Commit(); 87 | 88 | meshCollectorBuilder.Destroy(); 89 | 90 | // Setting the color of the MeshCollector we just created 91 | MeshCollector meshCollector = (MeshCollector)nXObject; 92 | MeshCollectorDisplayDefaults meshCollectorDisplayDefaults1; 93 | meshCollectorDisplayDefaults1 = meshCollector.GetMeshDisplayDefaults(); 94 | 95 | // we set the color as label * 10 to make a distinction between the colors. The maximum color number is 216, therefore we take the modulus to not exceed this numer (eg. 15%4 -> 3) 96 | meshCollectorDisplayDefaults1.Color = NXColor.Factory._Get((maxLabel * 10) % 216); //workFemPart.Colors.Find("Smoke Gray"); 97 | 98 | meshCollectorDisplayDefaults1.Dispose(); 99 | } 100 | } 101 | 102 | } -------------------------------------------------------------------------------- /PreProcessing/CreateNodes.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theScriptingEngineer/NXOpen-CAE/c35f3386f412d551f0527899f03e4ecca8542e81/PreProcessing/CreateNodes.cs -------------------------------------------------------------------------------- /PreProcessing/ForceBC.cs: -------------------------------------------------------------------------------- 1 | namespace TheScriptingEngineer 2 | { 3 | using System; 4 | using System.IO; // for path operations 5 | using System.Collections.Generic; // for lists 6 | using NXOpen; // so we can use NXOpen functionality 7 | using NXOpen.CAE; // so we don't need to start everything with NXOpen.CAE 8 | using NXOpenUI; 9 | using NXOpen.UF; 10 | using NXOpen.Utilities; 11 | 12 | public class ForceBC 13 | { 14 | // global variables used throughout 15 | public static Session theSession = Session.GetSession(); 16 | public static ListingWindow theLW = theSession.ListingWindow; 17 | public static BasePart basePart = theSession.Parts.BaseWork; 18 | 19 | public static void Main(string[] args) 20 | { 21 | theLW.Open(); 22 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 23 | 24 | // open file 25 | try 26 | { 27 | theLW.WriteFullline("Opening file " + args[0]); 28 | PartLoadStatus partLoadStatus; 29 | basePart = theSession.Parts.OpenActiveDisplay(args[0], DisplayPartOption.ReplaceExisting, out partLoadStatus); 30 | } 31 | catch (System.Exception) 32 | { 33 | theLW.WriteFullline("The file " + args[0] + " could not be opened!"); 34 | return; 35 | } 36 | 37 | CreateNodalForce(1871, 0, 0, -9810, "DeckLoadPS1"); 38 | CreateNodalForce(1948, 0, 0, -9810, "DeckLoadPS2"); 39 | CreateNodalForce(1908, 0, 0, -9810, "DeckLoadPS3"); 40 | 41 | CreateNodalForce(1870, 0, 0, -9810, "DeckLoadSB1"); 42 | CreateNodalForce(1938, 0, 0, -9810, "DeckLoadSB2"); 43 | CreateNodalForce(1907, 0, 0, -9810, "DeckLoadSB3"); 44 | 45 | CreateNodalForce(1882, 0, 0, -9810, "DeckLoadCenter1"); 46 | CreateNodalForce(1927, 0, 0, -9810, "DeckLoadCenter2"); 47 | CreateNodalForce(1918, 0, 0, -9810, "DeckLoadCenter3"); 48 | 49 | CreateNodalForce(3810, 0, 0, 9810, "BottomLoadPS1"); 50 | CreateNodalForce(3692, 0, 0, 9810, "BottomLoadPS2"); 51 | CreateNodalForce(3739, 0, 0, 9810, "BottomLoadPS3"); 52 | 53 | CreateNodalForce(3649, 0, 0, 9810, "BottomLoadSB1"); 54 | CreateNodalForce(3684, 0, 0, 9810, "BottomLoadSB2"); 55 | CreateNodalForce(3710, 0, 0, 9810, "BottomLoadSB3"); 56 | 57 | CreateNodalForce(3773, 0, 0, 9810, "BottomLoadCenter1"); 58 | CreateNodalForce(3668, 0, 0, 9810, "BottomLoadCenter2"); 59 | CreateNodalForce(3705, 0, 0, 9810, "BottomLoadCenter3"); 60 | 61 | CreateConstraint(1969, 0, 0, 0, -777777, -777777, -777777, "XYZ_Fixed"); 62 | CreateConstraint(2010, -777777, 0, 0, -777777, -777777, -777777, "YZ_Fixed"); 63 | CreateConstraint(2012, -777777, -777777, 0, -777777, -777777, -777777, "Z_Fixed"); 64 | 65 | // save the file 66 | theLW.WriteFullline("Saving file " + args[0]); 67 | basePart.Save(BasePart.SaveComponents.True, BasePart.CloseAfterSave.True); 68 | } 69 | 70 | /// 71 | /// Create a constraint on a single node with givel label, using the given settings and name. 72 | /// 73 | /// The array of ResultType used in the envelope 74 | /// Imposed displacement in global X direction for the constraint. 0: fixed, -777777: free. 75 | /// Imposed displacement in global Y direction for the constraint. 0: fixed, -777777: free. 76 | /// Imposed displacement in global Z direction for the constraint. 0: fixed, -777777: free. 77 | /// Imposed rotation in global X direction for the constraint. 0: fixed, -777777: free. 78 | /// Imposed rotation in global Y direction for the constraint. 0: fixed, -777777: free. 79 | /// Imposed rotation in global Z direction for the constraint. 0: fixed, -777777: free. 80 | /// Name of the constraint. 81 | /// Returns the created constraint object. 82 | public static SimBC CreateConstraint(int nodeLabel, double dx, double dy, double dz, double rx, double ry, double rz, string constraintName) 83 | { 84 | if (basePart as SimPart == null) 85 | { 86 | // caePart is not a SimPart 87 | theLW.WriteFullline("This program needs to start from a .sim file. Exiting"); 88 | return null; 89 | } 90 | SimPart simPart = (SimPart)basePart; 91 | SimSimulation simSimulation = simPart.Simulation; 92 | 93 | // set solution to inactive so constraint is not automatically added upon creation 94 | simPart.Simulation.ActiveSolution = null; 95 | 96 | // Check if constraint already exists 97 | SimBCBuilder simBCBuilder; 98 | SimConstraint[] simConstraints = simPart.Simulation.Constraints.ToArray(); 99 | SimConstraint simConstraint = Array.Find(simConstraints, constraint => constraint.Name.ToLower() == constraintName.ToLower()); 100 | if (simConstraint == null) 101 | { 102 | // no constraint with the given name, thus creating the constraint 103 | simBCBuilder = simSimulation.CreateBcBuilderForConstraintDescriptor("UserDefinedDisplacementConstraint", constraintName); 104 | } 105 | else 106 | { 107 | // a constraint with the given name already exists therefore editing the constraint 108 | simBCBuilder = simSimulation.CreateBcBuilderForBc(simConstraint); 109 | } 110 | 111 | PropertyTable propertyTable = simBCBuilder.PropertyTable; 112 | NXOpen.Fields.FieldExpression fieldExpression1 = propertyTable.GetScalarFieldPropertyValue("DOF1"); 113 | NXOpen.Fields.FieldExpression fieldExpression2 = propertyTable.GetScalarFieldPropertyValue("DOF2"); 114 | NXOpen.Fields.FieldExpression fieldExpression3 = propertyTable.GetScalarFieldPropertyValue("DOF3"); 115 | NXOpen.Fields.FieldExpression fieldExpression4 = propertyTable.GetScalarFieldPropertyValue("DOF4"); 116 | NXOpen.Fields.FieldExpression fieldExpression5 = propertyTable.GetScalarFieldPropertyValue("DOF5"); 117 | NXOpen.Fields.FieldExpression fieldExpression6 = propertyTable.GetScalarFieldPropertyValue("DOF6"); 118 | 119 | Unit unit1 = (Unit)simPart.UnitCollection.FindObject("MilliMeter"); 120 | NXOpen.Fields.FieldVariable[] indepVarArray1 = new NXOpen.Fields.FieldVariable[0]; 121 | fieldExpression1.EditFieldExpression(dx.ToString(), unit1, indepVarArray1, false); 122 | propertyTable.SetScalarFieldPropertyValue("DOF1", fieldExpression1); 123 | 124 | NXOpen.Fields.FieldVariable[] indepVarArray2 = new NXOpen.Fields.FieldVariable[0]; 125 | fieldExpression2.EditFieldExpression(dy.ToString(), unit1, indepVarArray2, false); 126 | propertyTable.SetScalarFieldPropertyValue("DOF2", fieldExpression2); 127 | 128 | NXOpen.Fields.FieldVariable[] indepVarArray3 = new NXOpen.Fields.FieldVariable[0]; 129 | fieldExpression3.EditFieldExpression(dz.ToString(), unit1, indepVarArray3, false); 130 | propertyTable.SetScalarFieldPropertyValue("DOF3", fieldExpression3); 131 | 132 | NXOpen.Unit unit2 = (NXOpen.Unit)simPart.UnitCollection.FindObject("Degrees"); 133 | NXOpen.Fields.FieldVariable[] indepVarArray4 = new NXOpen.Fields.FieldVariable[0]; 134 | fieldExpression4.EditFieldExpression(rx.ToString(), unit2, indepVarArray4, false); 135 | propertyTable.SetScalarFieldPropertyValue("DOF4", fieldExpression4); 136 | 137 | NXOpen.Fields.FieldVariable[] indepVarArray5 = new NXOpen.Fields.FieldVariable[0]; 138 | fieldExpression5.EditFieldExpression(ry.ToString(), unit2, indepVarArray5, false); 139 | propertyTable.SetScalarFieldPropertyValue("DOF5", fieldExpression5); 140 | 141 | NXOpen.Fields.FieldVariable[] indepVarArray6 = new NXOpen.Fields.FieldVariable[0]; 142 | fieldExpression6.EditFieldExpression(rz.ToString(), unit2, indepVarArray6, false); 143 | propertyTable.SetScalarFieldPropertyValue("DOF6", fieldExpression6); 144 | 145 | SetManager setManager = simBCBuilder.TargetSetManager; 146 | 147 | FENode fENode = simPart.Simulation.Femodel.FenodeLabelMap.GetNode(nodeLabel); 148 | if (fENode == null) 149 | { 150 | theLW.WriteFullline("No node with label " + nodeLabel + " found in the model! Constraint not created"); 151 | return null; 152 | } 153 | SetObject[] objects = new SetObject[1]; 154 | objects[0].Obj = fENode; 155 | objects[0].SubType = CaeSetObjectSubType.None; 156 | objects[0].SubId = 0; 157 | setManager.SetTargetSetMembers(0, CaeSetGroupFilterType.Node, objects); 158 | 159 | SimBC simBC = simBCBuilder.CommitAddBc(); 160 | 161 | simBCBuilder.Destroy(); 162 | 163 | return simBC; 164 | } 165 | 166 | /// 167 | /// Create a nodal force on a node with givel label, using the given force components and default name. 168 | /// 169 | /// The array of ResultType used in the envelope 170 | /// X component of the force in Newton and global X direction 171 | /// Y component of the force in Newton and global y direction 172 | /// Z component of the force in Newton and global Z direction 173 | /// Returns the created force object. 174 | public static SimBC CreateNodalForce(int nodeLabel, double fx, double fy, double fz) 175 | { 176 | string defaultName = "Load(" + nodeLabel.ToString() + ")"; 177 | return CreateNodalForce(nodeLabel, fx, fy, fz, defaultName); 178 | } 179 | 180 | 181 | /// 182 | /// Create a nodal force on a node with givel label, using the given force components and name. 183 | /// 184 | /// The array of ResultType used in the envelope 185 | /// X component of the force in Newton and global X direction 186 | /// Y component of the force in Newton and global y direction 187 | /// Z component of the force in Newton and global Z direction 188 | /// Name of the nodal force. 189 | /// Returns the created force object. 190 | public static SimBC CreateNodalForce(int nodeLabel, double fx, double fy, double fz, string forceName) 191 | { 192 | if (basePart as SimPart == null) 193 | { 194 | // caePart is not a SimPart 195 | theLW.WriteFullline("This program needs to start from a .sim file. Exiting"); 196 | return null; 197 | } 198 | SimPart simPart = (SimPart)basePart; 199 | SimSimulation simSimulation = simPart.Simulation; 200 | 201 | // set solution to inactive so load is not automatically added upon creation 202 | simPart.Simulation.ActiveSolution = null; 203 | 204 | // Check if load already exists 205 | SimBCBuilder simBCBuilder; 206 | SimLoad[] simLoads = simPart.Simulation.Loads.ToArray(); 207 | SimLoad simLoad = Array.Find(simLoads, load => load.Name.ToLower() == forceName.ToLower()); 208 | if (simLoad == null) 209 | { 210 | // no load with the given name, thus creating the load 211 | simBCBuilder = simSimulation.CreateBcBuilderForLoadDescriptor("ComponentForceField", forceName); 212 | } 213 | else 214 | { 215 | // a load with the given name already exists therefore editing the load 216 | simBCBuilder = simSimulation.CreateBcBuilderForBc(simLoad); 217 | } 218 | 219 | PropertyTable propertyTable = simBCBuilder.PropertyTable; 220 | SetManager setManager = simBCBuilder.TargetSetManager; 221 | 222 | FENode fENode = simPart.Simulation.Femodel.FenodeLabelMap.GetNode(nodeLabel); 223 | if (fENode == null) 224 | { 225 | theLW.WriteFullline("No node with label " + nodeLabel + " found in the model! Force not created"); 226 | return null; 227 | } 228 | SetObject[] objects = new SetObject[1]; 229 | objects[0].Obj = fENode; 230 | objects[0].SubType = CaeSetObjectSubType.None; 231 | objects[0].SubId = 0; 232 | setManager.SetTargetSetMembers(0, CaeSetGroupFilterType.Node, objects); 233 | 234 | NXOpen.Fields.VectorFieldWrapper vectorFieldWrapper1 = propertyTable.GetVectorFieldWrapperPropertyValue("CartesianMagnitude"); 235 | 236 | Unit unit1 = (NXOpen.Unit)simPart.UnitCollection.FindObject("Newton"); 237 | Expression expression1 = simPart.Expressions.CreateSystemExpressionWithUnits(fx.ToString(), unit1); 238 | Expression expression2 = simPart.Expressions.CreateSystemExpressionWithUnits(fy.ToString(), unit1); 239 | Expression expression3 = simPart.Expressions.CreateSystemExpressionWithUnits(fz.ToString(), unit1); 240 | 241 | NXOpen.Fields.FieldManager fieldManager1 = (NXOpen.Fields.FieldManager)simPart.FindObject("FieldManager"); 242 | Expression[] expressions1 = new Expression[3]; 243 | expressions1[0] = expression1; 244 | expressions1[1] = expression2; 245 | expressions1[2] = expression3; 246 | NXOpen.Fields.VectorFieldWrapper vectorFieldWrapper = fieldManager1.CreateVectorFieldWrapperWithExpressions(expressions1); 247 | 248 | propertyTable.SetVectorFieldWrapperPropertyValue("CartesianMagnitude", vectorFieldWrapper); 249 | propertyTable.SetTablePropertyWithoutValue("CylindricalMagnitude"); 250 | NXOpen.Fields.VectorFieldWrapper nullNXOpen_Fields_VectorFieldWrapper = null; 251 | propertyTable.SetVectorFieldWrapperPropertyValue("CylindricalMagnitude", nullNXOpen_Fields_VectorFieldWrapper); 252 | propertyTable.SetTablePropertyWithoutValue("SphericalMagnitude"); 253 | propertyTable.SetVectorFieldWrapperPropertyValue("SphericalMagnitude", nullNXOpen_Fields_VectorFieldWrapper); 254 | propertyTable.SetTablePropertyWithoutValue("DistributionField"); 255 | NXOpen.Fields.ScalarFieldWrapper nullNXOpen_Fields_ScalarFieldWrapper = null; 256 | propertyTable.SetScalarFieldWrapperPropertyValue("DistributionField", nullNXOpen_Fields_ScalarFieldWrapper); 257 | propertyTable.SetTablePropertyWithoutValue("ComponentsDistributionField"); 258 | propertyTable.SetVectorFieldWrapperPropertyValue("ComponentsDistributionField", nullNXOpen_Fields_VectorFieldWrapper); 259 | 260 | SimBC simBC = simBCBuilder.CommitAddBc(); 261 | 262 | simBCBuilder.Destroy(); 263 | 264 | return simBC; 265 | } 266 | 267 | 268 | /// 269 | /// Create a nodal moment on a node with givel label, using the given moment components and name. 270 | /// 271 | /// The array of ResultType used in the envelope 272 | /// X component of the moment in NewtonMeter and global X direction 273 | /// Y component of the moment in NewtonMeter and global y direction 274 | /// Z component of the moment in NewtonMeter and global Z direction 275 | /// Name of the nodal force. 276 | /// Returns the created force object. 277 | public static SimBC CreateNodalMoment(int nodeLabel, double mx, double my, double mz, string momentName) 278 | { 279 | if (basePart as SimPart == null) 280 | { 281 | // caePart is not a SimPart 282 | theLW.WriteFullline("This program needs to start from a .sim file. Exiting"); 283 | return null; 284 | } 285 | SimPart simPart = (SimPart)basePart; 286 | SimSimulation simSimulation = simPart.Simulation; 287 | 288 | // set solution to inactive so load is not automatically added upon creation 289 | simPart.Simulation.ActiveSolution = null; 290 | 291 | // Check if load already exists 292 | SimBCBuilder simBCBuilder; 293 | SimLoad[] simLoads = simPart.Simulation.Loads.ToArray(); 294 | SimLoad simLoad = Array.Find(simLoads, load => load.Name.ToLower() == momentName.ToLower()); 295 | if (simLoad == null) 296 | { 297 | // no load with the given name, thus creating the load 298 | simBCBuilder = simSimulation.CreateBcBuilderForLoadDescriptor("ComponentMomentField", momentName); 299 | } 300 | else 301 | { 302 | // a load with the given name already exists therefore editing the load 303 | simBCBuilder = simSimulation.CreateBcBuilderForBc(simLoad); 304 | } 305 | 306 | PropertyTable propertyTable = simBCBuilder.PropertyTable; 307 | SetManager setManager = simBCBuilder.TargetSetManager; 308 | 309 | FENode fENode = simPart.Simulation.Femodel.FenodeLabelMap.GetNode(nodeLabel); 310 | if (fENode == null) 311 | { 312 | theLW.WriteFullline("No node with label " + nodeLabel + " found in the model! Force not created"); 313 | return null; 314 | } 315 | SetObject[] objects = new SetObject[1]; 316 | objects[0].Obj = fENode; 317 | objects[0].SubType = CaeSetObjectSubType.None; 318 | objects[0].SubId = 0; 319 | setManager.SetTargetSetMembers(0, CaeSetGroupFilterType.Node, objects); 320 | 321 | NXOpen.Fields.VectorFieldWrapper vectorFieldWrapper1 = propertyTable.GetVectorFieldWrapperPropertyValue("CartesianMagnitude"); 322 | 323 | Unit unit1 = (NXOpen.Unit)simPart.UnitCollection.FindObject("NewtonMeter"); 324 | Expression expression1 = simPart.Expressions.CreateSystemExpressionWithUnits(mx.ToString(), unit1); 325 | Expression expression2 = simPart.Expressions.CreateSystemExpressionWithUnits(my.ToString(), unit1); 326 | Expression expression3 = simPart.Expressions.CreateSystemExpressionWithUnits(mz.ToString(), unit1); 327 | 328 | NXOpen.Fields.FieldManager fieldManager1 = (NXOpen.Fields.FieldManager)simPart.FindObject("FieldManager"); 329 | Expression[] expressions1 = new Expression[3]; 330 | expressions1[0] = expression1; 331 | expressions1[1] = expression2; 332 | expressions1[2] = expression3; 333 | NXOpen.Fields.VectorFieldWrapper vectorFieldWrapper = fieldManager1.CreateVectorFieldWrapperWithExpressions(expressions1); 334 | 335 | propertyTable.SetVectorFieldWrapperPropertyValue("CartesianMagnitude", vectorFieldWrapper); 336 | propertyTable.SetTablePropertyWithoutValue("CylindricalMagnitude"); 337 | NXOpen.Fields.VectorFieldWrapper nullNXOpen_Fields_VectorFieldWrapper = null; 338 | propertyTable.SetVectorFieldWrapperPropertyValue("CylindricalMagnitude", nullNXOpen_Fields_VectorFieldWrapper); 339 | propertyTable.SetTablePropertyWithoutValue("SphericalMagnitude"); 340 | propertyTable.SetVectorFieldWrapperPropertyValue("SphericalMagnitude", nullNXOpen_Fields_VectorFieldWrapper); 341 | propertyTable.SetTablePropertyWithoutValue("DistributionField"); 342 | NXOpen.Fields.ScalarFieldWrapper nullNXOpen_Fields_ScalarFieldWrapper = null; 343 | propertyTable.SetScalarFieldWrapperPropertyValue("DistributionField", nullNXOpen_Fields_ScalarFieldWrapper); 344 | propertyTable.SetTablePropertyWithoutValue("ComponentsDistributionField"); 345 | propertyTable.SetVectorFieldWrapperPropertyValue("ComponentsDistributionField", nullNXOpen_Fields_VectorFieldWrapper); 346 | 347 | SimBC simBC = simBCBuilder.CommitAddBc(); 348 | 349 | simBCBuilder.Destroy(); 350 | 351 | return simBC; 352 | } 353 | } 354 | } -------------------------------------------------------------------------------- /PreProcessing/ListBucklingDirections.cs: -------------------------------------------------------------------------------- 1 | // This journal list the buckling directions as they should be provided for buckling analysis. 2 | // In the group there should be 3 points (Menu -> Insert -> Model preparation - > Point) which define the stiffener direction and the plane. 3 | // The 2 points with color 162 (Deep brown, rgb 102 51 0) define the stiffener direction (longitudinal direction) in terms of buckling. 4 | // The 3rd point only defines the plane. The direction normal to the stiffener in the plane is determined and listed as the transverse direction in terms of buckling 5 | 6 | namespace TheScriptingEngineer 7 | { 8 | using System; 9 | using System.IO; // for path operations 10 | using System.Collections.Generic; // for lists 11 | using NXOpen; // so we can use NXOpen functionality 12 | using NXOpen.CAE; // so we don't need to start everything with NXOpen.CAE 13 | using NXOpenUI; 14 | using NXOpen.UF; 15 | using NXOpen.Utilities; 16 | using NXOpen.VectorArithmetic; 17 | 18 | public class ListBuckling 19 | { 20 | // global variables used throughout 21 | public static Session theSession = Session.GetSession(); 22 | public static ListingWindow theLW = theSession.ListingWindow; 23 | public static BasePart basePart = theSession.Parts.BaseWork; 24 | 25 | public static void Main(string[] args) 26 | { 27 | theLW.Open(); 28 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 29 | 30 | NXOpen.CAE.CaePart caePart = (NXOpen.CAE.CaePart)basePart; 31 | 32 | // One could reposition the model (eg afem) to get the buckling directions for different positions of certain parts. 33 | 34 | ListBucklingDirections(caePart); 35 | } 36 | 37 | public static void ListBucklingDirections(NXOpen.CAE.CaePart caePart, int stiffenerColor = 162) 38 | { 39 | foreach (NXOpen.CAE.CaeGroup group in caePart.CaeGroups) 40 | { 41 | if (group.Name.Contains("Bcklng")) 42 | { 43 | List points = new List(); 44 | foreach (TaggedObject entity in group.GetEntities()) 45 | { 46 | if (entity is NXOpen.Point) 47 | { 48 | points.Add((NXOpen.Point)entity); 49 | } 50 | } 51 | 52 | if (points.Count != 3) 53 | { 54 | theLW.WriteFullline("Group " + group.Name + " contains " + points.Count.ToString() + ". Should be 3."); 55 | continue; 56 | } 57 | 58 | // get the points with color 162 which define the stiffener dirction 59 | List stiffenerPoints = new List(); 60 | foreach (NXOpen.Point pt in points) 61 | { 62 | if (pt.Color == stiffenerColor) 63 | { 64 | stiffenerPoints.Add(pt); 65 | } 66 | } 67 | 68 | if (stiffenerPoints.Count != 2) 69 | { 70 | theLW.WriteFullline("Group " + group.Name + " contains " + stiffenerPoints.Count.ToString() + " points with color" + stiffenerColor.ToString() + "(Deep brown). Should be 2."); 71 | continue; 72 | } 73 | 74 | // Note using the using NXOpen.VectorArithmetic which has built in functions 75 | // Create the stiffener vector 76 | Vector3 stiffenerVector = GetDirectionFromPoints(stiffenerPoints); 77 | 78 | // Create another vector which defines the plane of the 3 points together with the stiffener vector 79 | points.Remove(stiffenerPoints[0]); 80 | Vector3 vectorInPlane = GetDirectionFromPoints(points); 81 | 82 | // The cross product is the normal vector of the plane 83 | Vector3 normal = stiffenerVector.Cross(vectorInPlane); 84 | 85 | // The cross of hte normal and the stiffener vector is in the plane and normal to the stiffener vector 86 | Vector3 transverse = stiffenerVector.Cross(normal); 87 | 88 | theLW.WriteFullline(group.Name + ": Longitudinal: [" + stiffenerVector.x.ToString("0.###") + ", " + stiffenerVector.y.ToString("0.###") + ", " + stiffenerVector.z.ToString("0.###") + 89 | "] Transverse: [" + transverse.x.ToString("0.###") + ", " + transverse.y.ToString("0.###") + ", " + transverse.z.ToString("0.###") + "]"); 90 | } 91 | } 92 | } 93 | 94 | public static Vector3 GetDirectionFromPoints(List points) 95 | { 96 | // Note using the using NXOpen.VectorArithmetic which has built in functions 97 | if (points.Count != 2) 98 | { 99 | theLW.WriteFullline("GetDirectionFromPoints only works with 2 points. " + points.Count.ToString() + "were given."); 100 | return new Vector3(0, 0, 0); 101 | } 102 | 103 | Vector3 vector = new Vector3(points[1].Coordinates.X - points[0].Coordinates.X, points[1].Coordinates.Y - points[0].Coordinates.Y, points[1].Coordinates.Z - points[0].Coordinates.Z); 104 | vector.Normalize(); 105 | 106 | return vector; 107 | } 108 | } 109 | } -------------------------------------------------------------------------------- /PreProcessing/RBE3Creation.cs: -------------------------------------------------------------------------------- 1 | namespace TheScriptingEngineer 2 | { 3 | using System; 4 | using System.IO; // for path operations 5 | using System.Collections.Generic; // for lists 6 | using NXOpen; // so we can use NXOpen functionality 7 | using NXOpen.CAE; // so we don't need to start everything with NXOpen.CAE 8 | using NXOpenUI; 9 | using NXOpen.UF; 10 | using NXOpen.Utilities; 11 | 12 | public class CreateRBE3 13 | { 14 | // global variables used throughout 15 | public static Session theSession = Session.GetSession(); 16 | public static UFSession theUFSession = UFSession.GetUFSession(); 17 | public static ListingWindow theLW = theSession.ListingWindow; 18 | public static BasePart basePart = theSession.Parts.BaseWork; 19 | 20 | public static void Main(string[] args) 21 | { 22 | // entrypoint for NX 23 | theLW.Open(); 24 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 25 | 26 | int nodeLabel = 1; 27 | CreateRBE3NodeToRecipe("PSAftPush", nodeLabel, new string[] {"PSAftPush1", "PSAftPush2", "PSAftPush3", "PSAftPush4", "PSAftPush5", "PSAftPush6", "PSAftPush7", "PSAftPush8", "PSAftPush9", "PSAftPush10", "PSAftPush11", "PSAftPush12"}); 28 | CreateRBE3NodeToRecipe("PSFwdPush", nodeLabel + 1, new string[] {"PSFwdPush1", "PSFwdPush2", "PSFwdPush3", "PSFwdPush4", "PSFwdPush5", "PSFwdPush6", "PSFwdPush7", "PSFwdPush8", "PSFwdPush9", "PSFwdPush10", "PSFwdPush11", "PSFwdPush12"}); 29 | CreateRBE3NodeToRecipe("SBFwdPush", nodeLabel + 2, new string[] {"SBFwdPush1", "SBFwdPush2", "SBFwdPush3", "SBFwdPush4", "SBFwdPush5", "SBFwdPush6", "SBFwdPush7", "SBFwdPush8", "SBFwdPush9", "SBFwdPush10", "SBFwdPush11", "SBFwdPush12"}); 30 | CreateRBE3NodeToRecipe("SBAftPush", nodeLabel + 3, new string[] {"SBAftPush1", "SBAftPush2", "SBAftPush3", "SBAftPush4", "SBAftPush5", "SBAftPush6", "SBAftPush7", "SBAftPush8", "SBAftPush9", "SBAftPush10", "SBAftPush11", "SBAftPush12"}); 31 | 32 | nodeLabel = 21; 33 | CreateRBE3NodeToRecipe("PSAftLowerGuide", nodeLabel, new string[] { "PSAftLowerGuideTarget" }); 34 | CreateRBE3NodeToRecipe("PSFwdLowerGuide", nodeLabel + 1, new string[] { "PSFwdLowerGuideTarget" }); 35 | CreateRBE3NodeToRecipe("SBFwdLowerGuide", nodeLabel + 2, new string[] { "SBFwdLowerGuideTarget" }); 36 | CreateRBE3NodeToRecipe("SBAftLowerGuide", nodeLabel + 3, new string[] { "SBAftLowerGuideTarget" }); 37 | 38 | } 39 | 40 | /// 41 | /// Creates an RBE3 node and assigns it to a specified connection recipe. 42 | /// 43 | /// The name to assign to the created mesh. 44 | /// The label of the source node for the RBE3 element. 45 | /// 46 | /// An array of strings representing the names of the target connection recipes. 47 | /// Each recipe specifies the target nodes for the RBE3 connection. 48 | /// 49 | /// 50 | /// Thrown if the commit operation fails, typically because the specified nodes do not exist. 51 | /// 52 | /// 53 | /// This method performs the following tasks: 54 | /// 55 | /// Creates an RBE3 element using the specified source node and target connection recipes. 56 | /// Associates the created RBE3 element with a specified mesh collector. 57 | /// Attempts to move the resulting mesh to a new mesh collector if applicable. 58 | /// Handles errors in target recipe resolution or commit operation gracefully with logs. 59 | /// 60 | /// 61 | public static void CreateRBE3NodeToRecipe(string name, int nodeLabel, string[] connectionRecipeNames) 62 | { 63 | string collectorName = "myCollectorName"; 64 | BaseFemPart baseFemPart = (BaseFemPart)theSession.Parts.BaseWork; 65 | BaseFEModel baseFEModel = baseFemPart.BaseFEModel; 66 | CAEConnectionBuilder cAEConnectionBuilder = baseFEModel.CaeConnections.CreateConnectionBuilder(null); 67 | cAEConnectionBuilder.ElementType.ElementDimension = ElementTypeBuilder.ElementType.Connection; 68 | cAEConnectionBuilder.ElementTypeRbe3.ElementDimension = ElementTypeBuilder.ElementType.Spider; 69 | cAEConnectionBuilder.ElementType.ElementTypeName = "RBE3"; 70 | 71 | MeshCollector meshCollector = GetMeshCollector(collectorName); 72 | cAEConnectionBuilder.ElementType.DestinationCollector.ElementContainer = meshCollector; 73 | cAEConnectionBuilder.ElementTypeRbe3.DestinationCollector.ElementContainer = meshCollector; 74 | 75 | TaggedObject[] sourceObjects = new TaggedObject[1]; 76 | sourceObjects[0] = baseFEModel.FenodeLabelMap.GetNode(nodeLabel); 77 | bool added1; 78 | added1 = cAEConnectionBuilder.SourceNodesSelection.Add(sourceObjects); 79 | 80 | TaggedObject[] targetObjects = new NXOpen.TaggedObject[connectionRecipeNames.Length]; 81 | SelectionRecipe[] allSelectionRecipes = baseFemPart.SelectionRecipes.ToArray(); 82 | for (int i = 0; i < connectionRecipeNames.Length; i++) 83 | { 84 | BoundingVolumeSelectionRecipe targetRecipe = (BoundingVolumeSelectionRecipe)Array.Find(allSelectionRecipes, recipe => recipe.Name == connectionRecipeNames[i]); 85 | if (targetRecipe == null) 86 | { 87 | theLW.WriteFullline("ERROR: ELEMENT NOT CREATED: Could not find " + connectionRecipeNames[i]); 88 | return; 89 | } 90 | targetObjects[i] = targetRecipe; 91 | } 92 | added1 = cAEConnectionBuilder.TargetNodesSelection.Add(targetObjects); 93 | 94 | try 95 | { 96 | cAEConnectionBuilder.Commit(); 97 | } 98 | catch (System.Exception) 99 | { 100 | theLW.WriteFullline("Error in commit. Do the nodes exist?"); 101 | throw; 102 | } 103 | 104 | Mesh mesh = cAEConnectionBuilder.Mesh; 105 | mesh.SetName(name); 106 | // move mesh to collector if it exists and cleanup 107 | MeshCollector newMeshCollector = GetMeshCollector(collectorName); 108 | MeshCollector oldMeshCollector; 109 | MeshManager meshManager = (MeshManager)baseFEModel.MeshManager; 110 | if (newMeshCollector != null) 111 | { 112 | oldMeshCollector = (MeshCollector)mesh.MeshCollector; 113 | meshManager.MoveMeshToNewCollector(mesh, false, oldMeshCollector, newMeshCollector); 114 | theSession.UpdateManager.AddToDeleteList(oldMeshCollector); 115 | } 116 | 117 | cAEConnectionBuilder.Destroy(); 118 | } 119 | 120 | 121 | /// 122 | /// Returns the MeshCollector with the given name, null otherwise. 123 | /// 124 | /// The name of the MeshCollector to return. 125 | /// The MeshCollector if found, the default value for T (null?) othwerwise. 126 | public static MeshCollector GetMeshCollector(string name) 127 | { 128 | BaseFemPart baseFemPart = (BaseFemPart)theSession.Parts.BaseWork; 129 | BaseFEModel baseFEModel = baseFemPart.BaseFEModel; 130 | IMeshCollector[] meshCollectors = baseFEModel.MeshManager.GetMeshCollectors(); 131 | MeshCollector meshCollector = (MeshCollector)Array.Find(meshCollectors, collector => collector.Name.ToLower() == name.ToLower()); 132 | return meshCollector; 133 | } 134 | 135 | /// 136 | /// Creates an RBE3 connection element linking a source selection recipe to one or more target selection recipes. 137 | /// 138 | /// The name of the source selection recipe. 139 | /// 140 | /// An array of strings representing the names of the target selection recipes. 141 | /// Each recipe specifies the target nodes for the RBE3 connection. 142 | /// 143 | /// 144 | /// This method performs the following tasks: 145 | /// 146 | /// Identifies the source and target selection recipes by their names. 147 | /// Creates an RBE3 connection element using the source recipe as the origin. 148 | /// Links the created connection element to the specified target recipes. 149 | /// Uses a predefined mesh collector for element storage. 150 | /// Logs errors if any selection recipe cannot be found. 151 | /// 152 | /// 153 | /// 154 | /// Propagated if there is an error during the commit operation. 155 | /// 156 | public static void CreateRBE3RecipeToRecipe(string sourceRecipeName, string[] connectionRecipeNames) 157 | { 158 | BaseFemPart baseFemPart = (BaseFemPart)theSession.Parts.BaseWork; 159 | BaseFEModel baseFEModel = (BaseFEModel)baseFemPart.BaseFEModel; 160 | CAEConnectionBuilder cAEConnectionBuilder = baseFEModel.CaeConnections.CreateConnectionBuilder(null); 161 | cAEConnectionBuilder.ElementType.ElementDimension = NXOpen.CAE.ElementTypeBuilder.ElementType.Connection; 162 | cAEConnectionBuilder.ElementTypeRbe3.ElementDimension = NXOpen.CAE.ElementTypeBuilder.ElementType.Spider; 163 | cAEConnectionBuilder.ElementType.ElementTypeName = "RBE3"; 164 | 165 | MeshCollector meshCollector = (MeshCollector)baseFEModel.MeshManager.FindObject("MeshCollector[myMeshCollectorName]"); 166 | cAEConnectionBuilder.ElementType.DestinationCollector.ElementContainer = meshCollector; 167 | cAEConnectionBuilder.ElementTypeRbe3.DestinationCollector.ElementContainer = meshCollector; 168 | 169 | SelectionRecipe[] allSelectionRecipes = baseFemPart.SelectionRecipes.ToArray(); 170 | BoundingVolumeSelectionRecipe sourceRecipe = (BoundingVolumeSelectionRecipe)Array.Find(allSelectionRecipes, recipe => recipe.Name == sourceRecipeName); 171 | if (sourceRecipe == null) 172 | { 173 | theLW.WriteFullline("Could not find " + sourceRecipeName); 174 | return; 175 | } 176 | 177 | NXOpen.TaggedObject[] sourceObjects = new NXOpen.TaggedObject[1]; 178 | sourceObjects[0] = sourceRecipe; 179 | bool added1; 180 | added1 = cAEConnectionBuilder.SourceNodesSelection.Add(sourceObjects); 181 | 182 | TaggedObject[] targetObjects = new NXOpen.TaggedObject[connectionRecipeNames.Length]; 183 | for (int i = 0; i < connectionRecipeNames.Length; i++) 184 | { 185 | BoundingVolumeSelectionRecipe targetRecipe = (BoundingVolumeSelectionRecipe)Array.Find(allSelectionRecipes, recipe => recipe.Name == connectionRecipeNames[i]); 186 | if (targetRecipe == null) 187 | { 188 | theLW.WriteFullline("Could not find " + connectionRecipeNames[i]); 189 | return; 190 | } 191 | targetObjects[i] = targetRecipe; 192 | } 193 | added1 = cAEConnectionBuilder.TargetNodesSelection.Add(targetObjects); 194 | 195 | cAEConnectionBuilder.Commit(); 196 | cAEConnectionBuilder.Destroy(); 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /PreProcessing/RepositionUsingExpressions.cs: -------------------------------------------------------------------------------- 1 | // If one has expressions defined in the mastercadpart to control the position of the components, 2 | // this script can update these expressions in the mastercadpart, starting from the sim part. 3 | // when combined with SolveSolution one can reposition and solve several postitions of the model in one run. 4 | 5 | namespace TheScriptingEngineer 6 | { 7 | using System; 8 | using System.IO; // for path operations 9 | using System.Collections.Generic; // for lists 10 | using NXOpen; // so we can use NXOpen functionality 11 | using NXOpen.CAE; // so we don't need to start everything with NXOpen.CAE 12 | using NXOpenUI; 13 | using NXOpen.UF; 14 | using NXOpen.Utilities; 15 | 16 | public class RepositionUsingExpressions 17 | { 18 | // global variables used throughout 19 | public static Session theSession = Session.GetSession(); 20 | public static ListingWindow theLW = theSession.ListingWindow; 21 | public static BasePart basePart = theSession.Parts.BaseWork; 22 | //public static SimSolveManager simSolveManager = SimSolveManager.GetSimSolveManager(theSession); 23 | 24 | public static void Main(string[] args) 25 | { 26 | theLW.Open(); 27 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 28 | 29 | // Check if running from a .sim part 30 | if (basePart as SimPart == null) 31 | { 32 | theLW.WriteFullline("RepositionUsingExpressions needs to start from a .sim file"); 33 | return; 34 | } 35 | 36 | SetExpressionsInMasterCadPart(new string[]{"main_boom_angle"}, new string[]{"Degrees"}, new double[]{0}); 37 | 38 | } 39 | 40 | /// 41 | /// This function updates an array of expressions in the mastercad part of an assembly fem. 42 | /// The values in the arrays need to be in order. 43 | /// Needs to start from a sim file. 44 | /// 45 | /// The array of expression names to update. Case-sensitive! 46 | /// The array of units for the expression. Need to be a known NX units. Case-sensitive! 47 | /// The array of new values for the expressions 48 | public static void SetExpressionsInMasterCadPart(string[] expressionNames, string[] expressionUnits, double[] expressionValues) 49 | { 50 | if (expressionNames.Length != expressionUnits.Length || expressionNames.Length != expressionValues.Length) 51 | { 52 | theLW.WriteFullline("Error in SetExpressionsInMasterCadPart. Arrays with names, units and values need to be the same length!"); 53 | return; 54 | } 55 | // assume start from .sim part 56 | SimPart simPart = (SimPart)basePart; 57 | 58 | // get the assyfempart 59 | AssyFemPart assyFemPart = (AssyFemPart)simPart.ComponentAssembly.RootComponent.GetChildren()[0].Prototype.OwningPart; 60 | 61 | // get the mastercadpart linked to the assyfem. 62 | Part masterCadPart = assyFemPart.MasterCadPart; 63 | PartLoadStatus partLoadStatus; 64 | if (masterCadPart == null) 65 | { 66 | masterCadPart = (Part)theSession.Parts.OpenActiveDisplay(assyFemPart.FullPathForAssociatedCadPart, DisplayPartOption.ReplaceExisting, out partLoadStatus); 67 | } 68 | 69 | // update the expression in the mastercadpart 70 | for (int i = 0; i < expressionNames.Length; i++) 71 | { 72 | theLW.WriteFullline("Updating " + expressionNames + " in " + masterCadPart.FullPath + " to value of " + expressionValues.ToString() + " " + expressionUnits); 73 | SetExpressionValue(masterCadPart, expressionNames[i], expressionUnits[i], expressionValues[i]); 74 | } 75 | 76 | 77 | // update the AssyFeModel 78 | theSession.Parts.SetActiveDisplay(assyFemPart, DisplayPartOption.ReplaceExisting, PartDisplayPartWorkPartOption.SameAsDisplay, out partLoadStatus); 79 | assyFemPart.BaseFEModel.UpdateFemodel(); 80 | 81 | // save the assyfempart after updating 82 | assyFemPart.Save(NXOpen.BasePart.SaveComponents.True,NXOpen.BasePart.CloseAfterSave.False); 83 | 84 | //return to the simpart (where we started from) 85 | theSession.Parts.SetActiveDisplay(simPart, DisplayPartOption.ReplaceExisting, PartDisplayPartWorkPartOption.SameAsDisplay, out partLoadStatus); 86 | } 87 | 88 | /// 89 | /// This function updates an expression within a part. 90 | /// 91 | /// The part in which to update the expression. 92 | /// The name of the expression to update. Case-sensitive! 93 | /// The unit of the expression. Needs to be one of the known NX units. Case-sensitive! 94 | /// The new value for the expression 95 | public static void SetExpressionValue(NXOpen.Part part, string expressionName, string expressionUnit, double expressionValue) 96 | { 97 | NXOpen.Expression expression; 98 | NXOpen.Unit unit; 99 | try 100 | { 101 | expression = ((NXOpen.Expression)part.Expressions.FindObject(expressionName)); 102 | unit = ((NXOpen.Unit)part.UnitCollection.FindObject(expressionUnit)); 103 | } 104 | catch (System.Exception) 105 | { 106 | theLW.WriteFullline("Error when trying to update " + expressionName + " with unit " + expressionUnit + ". Make sure the expression and unit exist"); 107 | return; 108 | } 109 | 110 | // only update if value is different. 111 | if (expression.Value != expressionValue) 112 | { 113 | part.Expressions.EditExpressionWithUnits(expression, unit, expressionValue.ToString()); 114 | } 115 | else 116 | { 117 | return; 118 | } 119 | 120 | NXOpen.Session.UndoMarkId markIdMakeUpToDate = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Make Up to Date"); 121 | 122 | NXOpen.NXObject[] objects = new NXOpen.NXObject[1]; 123 | objects[0] = expression; 124 | theSession.UpdateManager.MakeUpToDate(objects, markIdMakeUpToDate); 125 | 126 | NXOpen.Session.UndoMarkId markIdDoUpdate = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "NX update"); 127 | 128 | int nErrs1 = theSession.UpdateManager.DoUpdate(markIdDoUpdate); 129 | } 130 | } 131 | } -------------------------------------------------------------------------------- /PreProcessing/SolveSolution.cs: -------------------------------------------------------------------------------- 1 | // "c:\Program Files\Siemens\Simcenter 12.0\NXBIN\run_journal.exe" "C:\Users\Frederik\Documents\basicCsCourse\Section9\Program.cs" -args "C:\Users\Frederik\Documents\SC12\Section9\hullModelNX12_fem1_sim1.sim" 2 | // "c:\Program Files\Siemens\Simcenter3D_2022.1\NXBIN\run_journal.exe" "C:\Users\Frederik\Documents\basicCsCourse\Section9\Program.cs" -args "C:\Users\Frederik\Documents\SC2022\Section9\hullModelNX12_fem1_sim1.sim" 3 | 4 | namespace TheScriptingEngineer 5 | { 6 | using System; 7 | using System.IO; // for path operations 8 | using System.Collections.Generic; // for lists 9 | using NXOpen; // so we can use NXOpen functionality 10 | using NXOpen.CAE; // so we don't need to start everything with NXOpen.CAE 11 | using NXOpenUI; 12 | using NXOpen.UF; 13 | using NXOpen.Utilities; 14 | 15 | public class Solve 16 | { 17 | // global variables used throughout 18 | public static Session theSession = Session.GetSession(); 19 | public static ListingWindow theLW = theSession.ListingWindow; 20 | public static BasePart basePart = theSession.Parts.BaseWork; 21 | //public static SimSolveManager simSolveManager = SimSolveManager.GetSimSolveManager(theSession); 22 | 23 | public static void Main(string[] args) 24 | { 25 | theLW.Open(); 26 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 27 | 28 | if (args.Length == 0) 29 | { 30 | // no arguments passed 31 | // write some sort of a help 32 | theLW.WriteFullline("Need to pass the full path of the .sim file as a first argument."); 33 | theLW.WriteFullline("All additional parameters should be solutions to solve."); 34 | theLW.WriteFullline("If no additional parameters are passed, all solutions in the sim file are solved."); 35 | return; 36 | } 37 | // in batch, so need to open file 38 | // open the file with the first argument 39 | theLW.WriteFullline("Opening file " + args[0]); 40 | try 41 | { 42 | PartLoadStatus partLoadStatus; 43 | basePart = theSession.Parts.OpenActiveDisplay(args[0], DisplayPartOption.ReplaceExisting, out partLoadStatus); 44 | } 45 | catch (System.Exception) 46 | { 47 | theLW.WriteFullline("The file " + args[0] + " could not be opened!"); 48 | return; 49 | } 50 | 51 | // Check if running from a .sim part 52 | if (basePart as SimPart == null) 53 | { 54 | theLW.WriteFullline("SolveSolution needs to start from a .sim file"); 55 | return; 56 | } 57 | 58 | if (args.Length == 1) 59 | { 60 | // only one argument (file to open) so solve all solutions 61 | SolveAllSolutions(); 62 | } 63 | else 64 | { 65 | for (int i = 1; i < args.Length; i++) 66 | { 67 | // 2 or more arguments. Solve the solution for each argument. (skip arg[0] becasue that's the sim file) 68 | SolveSolution(args[i]); 69 | } 70 | } 71 | } 72 | 73 | /// This function solves a all solutions in a .sim file. 74 | public static void SolveAllSolutions() 75 | { 76 | // Note: don't loop over the solutions and solve. This will give a memory access violation error, but will still solve. 77 | // The error can be avoided by making the simSolveManager a global variable, so it's not on each call. 78 | theLW.WriteFullline("Solving all solutions:"); 79 | Int32 numsolutionssolved = -1; 80 | Int32 numsolutionsfailed = -1; 81 | Int32 numsolutionsskipped = -1; 82 | SimSolveManager simSolveManager = SimSolveManager.GetSimSolveManager(theSession); 83 | simSolveManager.SolveAllSolutions(SimSolution.SolveOption.Solve, SimSolution.SetupCheckOption.DoNotCheck, SimSolution.SolveMode.Foreground, false, out numsolutionssolved, out numsolutionsfailed, out numsolutionsskipped); 84 | } 85 | 86 | /// This function solves a single solution in a .sim file. 87 | /// NOTE: don't loop over the solutions and solve. This will give a memory access violation error, but will still solve. 88 | /// The error can be avoided by making the simSolveManager a global variable, so it's not recreated on each call. 89 | /// The name of the solution to solve. Case insensitive. 90 | public static void SolveSolution(string solutionName) 91 | { 92 | theLW.WriteFullline("Solving " + solutionName); 93 | SimPart simPart = (SimPart)basePart; 94 | 95 | // Get the requested solution 96 | SimSolution[] simSolutions = simPart.Simulation.Solutions.ToArray(); 97 | SimSolution simSolution = Array.Find(simSolutions, sol => sol.Name.ToLower() == solutionName.ToLower()); 98 | // check if requested solution has been found 99 | if (simSolution == null) 100 | { 101 | theLW.WriteFullline("Solution with name " + solutionName + " could not be found in " + simPart.FullPath); 102 | return; 103 | } 104 | 105 | // solve the solution 106 | SimSolution[] chain = new SimSolution[1]; 107 | chain[0] = simSolution; 108 | 109 | //NXOpen.CAE.SimSolveManager simSolveManager = NXOpen.CAE.SimSolveManager.GetSimSolveManager(theSession); 110 | Int32 numsolutionssolved = -1; 111 | Int32 numsolutionsfailed = -1; 112 | Int32 numsolutionsskipped = -1; 113 | // SimSolution.SolveMode.Foreground will make the code in this journal wait for the solve to finish. As a result, if you make multiple calls to SolveSolution, all solves will run sequentially 114 | // SimSolution.SolveMode.Background will kick off the solve and immediately continue the code in this journal. As a result, if you make multiple calls to SolveSolution, all solves will run in parallell 115 | SimSolveManager simSolveManager = SimSolveManager.GetSimSolveManager(theSession); 116 | simSolveManager.SolveChainOfSolutions(chain, SimSolution.SolveOption.Solve, SimSolution.SetupCheckOption.DoNotCheck, SimSolution.SolveMode.Foreground, out numsolutionssolved, out numsolutionsfailed, out numsolutionsskipped); 117 | 118 | // user feedback 119 | theLW.WriteFullline("Solved solution " + solutionName + ". Number solved: " + numsolutionssolved.ToString() + " failed: " + numsolutionsfailed.ToString() + " skipped: " + numsolutionsskipped.ToString()); 120 | } 121 | 122 | /// 123 | /// This function solves a .dat file by directly calling the nastran.exe executable. 124 | /// It takes the location of the nastran.exe executable form the environmental variable UGII_NX_NASTRAN. 125 | /// By directly calling the nastran executable, a standalone license for the executable is required! 126 | /// Running this with a desktop license wil result in an error: 127 | /// "Could not check out license for module: Simcenter Nastran Basic" 128 | /// 129 | /// The full path of the .dat file. 130 | public static void SolveDatFile(string datFile) 131 | { 132 | // get the location nastran.exe via the environmental variable 133 | string UGII_NX_NASTRAN = theSession.GetEnvironmentVariableValue("UGII_NX_NASTRAN"); 134 | theLW.WriteFullline(UGII_NX_NASTRAN); 135 | 136 | // process datFile for path and extension 137 | string fullDatFile = CreateFullPath(datFile, ".dat"); 138 | 139 | // create a process to run the nastran executable on the .dat file provided 140 | System.Diagnostics.Process process = new System.Diagnostics.Process(); 141 | process.StartInfo.FileName = UGII_NX_NASTRAN; 142 | process.StartInfo.Arguments = fullDatFile; 143 | // set the working directory to the directory in the .dat file. 144 | // otherwise the process starts from the location of the nastran.exe, which is very likely not writable by the user (only by the admin) 145 | process.StartInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(fullDatFile); 146 | 147 | // start the process 148 | process.Start(); 149 | theLW.WriteFullline("Solve started for" + fullDatFile); 150 | 151 | // wait for the process to finish 152 | process.WaitForExit(); 153 | theLW.WriteFullline("Solve finished."); 154 | } 155 | 156 | /// 157 | /// This function takes a filename and adds an extension and path of the part if not provided by the user. 158 | /// If the fileName contains an extension, this function leaves it untouched, othwerwise adds the provided extension, which defaults to .unv. 159 | /// If the fileName contains a path, this function leaves it untouched, otherwise adds the path of the BasePart as the path. 160 | /// 161 | /// The filename with or without path and .unv extension. 162 | /// Optional: The extension to add if missing. Defaults to .unv. 163 | /// A string with extension and path of basePart if the fileName parameter did not include a path. 164 | public static string CreateFullPath(string fileName, string extension = ".unv") 165 | { 166 | // check if .unv is included in fileName 167 | if (Path.GetExtension(fileName).Length == 0) 168 | { 169 | fileName = fileName + extension; 170 | } 171 | 172 | // check if path is included in fileName, if not add path of the .sim file 173 | string unvFilePath = Path.GetDirectoryName(fileName); 174 | if (unvFilePath == "") 175 | { 176 | // if the basePart file has never been saved, the next will give an error 177 | fileName = Path.Combine(Path.GetDirectoryName(basePart.FullPath), fileName); 178 | } 179 | 180 | return fileName; 181 | } 182 | } 183 | } -------------------------------------------------------------------------------- /Program.cs: -------------------------------------------------------------------------------- 1 | namespace TheScriptingEngineer 2 | { 3 | using System; 4 | using System.IO; // for path operations 5 | using System.Collections.Generic; // for lists 6 | using NXOpen; // so we can use NXOpen functionality 7 | using NXOpen.CAE; // so we don't need to start everything with NXOpen.CAE 8 | using NXOpenUI; 9 | using NXOpen.UF; 10 | using NXOpen.Utilities; 11 | 12 | public class Program 13 | { 14 | // global variables used throughout 15 | public static Session theSession = Session.GetSession(); 16 | public static UFSession theUFSession = UFSession.GetUFSession(); 17 | public static ListingWindow theLW = theSession.ListingWindow; 18 | public static BasePart basePart = theSession.Parts.BaseWork; 19 | 20 | public static void Main(string[] args) 21 | { 22 | // entrypoint for NX 23 | theLW.Open(); 24 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NXOpen-CAE 2 | 3 | This is a repository with NXOpen code for Siemens SimCenter 3D (aka NX CAE) in C# 4 | If you’re using my scripts in your daily work, saving you a lot of work and time, buy me a coffe so I can continue developing awesome scripts. 5 | [Buy me a coffe](https://www.buymeacoffee.com/theScriptingEngineer) 6 | 7 | ## Learning NXOpen 8 | I currently have 2 courses available for learning NXOpen. A beginner course in python to get you started by setting up your developer environment and teach some basic concepts: 9 | 10 | [Siemens NX beginner NXOpen course (Python)](https://www.udemy.com/course/siemens-nx-beginner-nxopen-course-python/?referralCode=DEE8FAB445765802FEDC) 11 | 12 | An in depth course walking you through a complete structural analysis, step by step, towardw a fully automated analysis from load/bc application till postprocessing: 13 | 14 | [SimCenter 3D basic NXOpen course (C#)](https://www.udemy.com/course/simcenter3d-basic-nxopen-course/?referralCode=4ABC27CFD7D2C57D220B%20) 15 | 16 | >Use the code **NXOPEN_PYTHON_SEP24** or **NXOPEN_CSHARP_SEP24** to get the current best price (replace *SEP* with the current 3 letter month eg. DEC if the current month is December) 17 | > 18 | 19 | If you’re using my scripts in your daily work, saving you a lot of work and time, buy me a coffe so I can continue developing awesome scripts. 20 | [Buy me a coffe](https://www.buymeacoffee.com/theScriptingEngineer) 21 | 22 | 23 | ## Documentation of the journals in this repository 24 | 25 | [NXOpen C# documentation](https://nxopencsdocumentation.thescriptingengineer.com/) 26 | 27 | ## Setup 28 | 29 | I'm using Visual Studio Code for creating my journals. 30 | 31 | ### SimCenter dll's for intellisense 32 | 33 | Because of copyright concerns I did not add the dll's required for intellisense to this repository. Also, you want to use the dll's from the specific SimCenter version that you are using. 34 | 35 | In order to have intellisense do either of the following: 36 | 1. Copy the following SimCenter dll's into the dlls folder: 37 | * NXOpen.dll 38 | * NXOpen.Utilities.dll 39 | * NXOpen.UF.dll 40 | * NXOpenUI.dll 41 | 2. Update the references in NXOpenCAE.csproj to point to the dll's for the specific version that you are using. 42 | 43 | ### VSCode +.NET framework 44 | 45 | This repository and the course on Udemy focus on journals only, meaning none of the code is compiled. 46 | Every file can run as a journal, without additional licenses (provided you have the licenses to perform the same operations in the GUI) 47 | Since I'm writing the journals on a linux machine, I'm referencing the netcoreapp3.1 as the targetframework, so I get a working intellisense. 48 | 49 | For windows users please visit my [course]((https://www.udemy.com/course/simcenter3d-basic-nxopen-course/?referralCode=4ABC27CFD7D2C57D220B%20)) or the excellent blog from [Jan Boetcher](https://www.ib-boettcher.de/en/post/nxopen-vscode/) 50 | -------------------------------------------------------------------------------- /Tools/CreateGroupsFromCAD.cs: -------------------------------------------------------------------------------- 1 | namespace TheScriptingEngineer 2 | { 3 | using System; 4 | using System.IO; // for path operations 5 | using System.Collections.Generic; // for lists 6 | using NXOpen; // so we can use NXOpen functionality 7 | using NXOpen.CAE; // so we don't need to start everything with NXOpen.CAE 8 | using NXOpenUI; 9 | using NXOpen.UF; 10 | using NXOpen.Utilities; 11 | using NXOpen.VectorArithmetic; 12 | 13 | public class CreateGroupsFromCAD 14 | { 15 | // global variables used throughout 16 | public static Session theSession = Session.GetSession(); 17 | public static ListingWindow theLW = theSession.ListingWindow; 18 | public static BasePart basePart = theSession.Parts.BaseWork; 19 | 20 | public static void Main(string[] args) 21 | { 22 | theLW.Open(); 23 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 24 | 25 | FemPart femPart = null; 26 | if (basePart as SimPart !=null) 27 | { 28 | // we started from a sim file 29 | SimPart simPart = (SimPart)basePart; 30 | CaePart caePart = simPart.FemPart; // ComponentAssembly.RootComponent.GetChildren()[0].Prototype.OwningPart; 31 | if (caePart as FemPart == null) 32 | { 33 | // simfile is linked to .afem file 34 | theLW.WriteFullline("Create groups from CAD does not support .afem files yet."); 35 | return; 36 | } 37 | 38 | femPart = (FemPart)caePart; 39 | } 40 | else if (basePart as AssyFemPart != null) 41 | { 42 | // we startef from a .afem file 43 | theLW.WriteFullline("Create groups from CAD does not support .afem files yet."); 44 | return; 45 | } 46 | else if (basePart as FemPart !=null) 47 | { 48 | // we started from a fem file 49 | femPart = (FemPart)basePart; 50 | } 51 | else 52 | { 53 | // not started from a cae part 54 | theLW.WriteFullline("Create groups does not work on non-cae parts"); 55 | return; 56 | } 57 | 58 | CreateGroupsFromNamedPlanes(femPart); 59 | } 60 | 61 | /// 62 | /// This function creates a group with faces and bodies for each named datum plane in the associated cad part. 63 | /// All named datum planes are collected from the associated cad part. 64 | /// Then for each datum plane a selection recipe is created, "centered around" the datum plane, with the faces and bodies. 65 | /// For each selection recipe a group is created and the selection recipe deleted. 66 | /// Groups are created instead of selection recipes because older versions of Simcenter cannot use selection recipes in post-processing. 67 | /// Function is idempotent. 68 | /// 69 | /// The part in whcih to create the groups. 70 | public static void CreateGroupsFromNamedPlanes(FemPart femPart) 71 | { 72 | // Get the associated cad part 73 | Part associatedCadPart = GetAssociatedCadPart(femPart); 74 | 75 | // Get an array of all named datum planes 76 | DatumPlane[] datumPlanes = GetNamedDatumPlanes(associatedCadPart); 77 | if (datumPlanes.Length == 0) 78 | { 79 | theLW.WriteFullline("No named datum planes found in " + associatedCadPart.Name); 80 | return; 81 | } 82 | 83 | theLW.WriteFullline("Found the following named datum planes in " + associatedCadPart.Name + ":"); 84 | foreach (DatumPlane item in datumPlanes) 85 | { 86 | theLW.WriteFullline(item.Feature.Name); 87 | } 88 | 89 | // Create selection recipe for each named datum plane 90 | SelectionRecipe[] selectionRecipes = new SelectionRecipe[datumPlanes.Length]; 91 | 92 | NXOpen.CAE.CaeSetGroupFilterType[] entitytypes = new NXOpen.CAE.CaeSetGroupFilterType[2]; 93 | entitytypes[0] = NXOpen.CAE.CaeSetGroupFilterType.GeomFace; 94 | entitytypes[1] = NXOpen.CAE.CaeSetGroupFilterType.GeomBody; 95 | for (int i = 0; i < datumPlanes.Length; i++) 96 | { 97 | selectionRecipes[i] = CreateSelectionRecipe(femPart, datumPlanes[i], entitytypes); 98 | } 99 | 100 | // Create a group for each recipe 101 | CaeGroup[] caeGroups = femPart.CaeGroups.ToArray(); 102 | for (int i = 0; i < datumPlanes.Length; i++) 103 | { 104 | TaggedObject[] taggedObjects = selectionRecipes[i].GetEntities(); 105 | if (taggedObjects.Length == 0) 106 | { 107 | theLW.WriteFullline("Recipe with name " + selectionRecipes[i].Name + " contains no items to put into a group"); 108 | continue; 109 | } 110 | 111 | CaeGroup caeGroup = Array.Find(caeGroups, group => group.Name.ToLower() == datumPlanes[i].Feature.Name.ToLower()); 112 | if (caeGroup == null) 113 | { 114 | // no group found with the feaure name, thus creating 115 | femPart.CaeGroups.CreateGroup(datumPlanes[i].Feature.Name, taggedObjects); 116 | } 117 | else 118 | { 119 | caeGroup.SetEntities(taggedObjects); 120 | } 121 | } 122 | 123 | femPart.SelectionRecipes.Delete(selectionRecipes); 124 | } 125 | 126 | /// 127 | /// This function creates a selection recipe around a given datum plane. 128 | /// Since a selection recipe is not infinite, the dimensions are hard coded, but can be easily adjusted. 129 | /// 130 | /// The part in whcih to create a selection recipe. 131 | /// A datum plane, which is used to define the selection recipe. 132 | /// An array of filters for the type of objects to add to the selection recipe. 133 | /// The created selection recipe. 134 | public static SelectionRecipe CreateSelectionRecipe(FemPart femPart, DatumPlane datumPlane, CaeSetGroupFilterType[] entitytypes) 135 | { 136 | double recipeThickness = 1; 137 | double recipeSize = 100000; 138 | 139 | Vector3 origin = new Vector3(datumPlane.Origin.X, datumPlane.Origin.Y, datumPlane.Origin.Z); // define origin also as vector3 so that we can use arithmetic 140 | Vector3 normal = new Vector3(datumPlane.Normal.X, datumPlane.Normal.Y, datumPlane.Normal.Z); 141 | 142 | Vector3 global = new Vector3(1.0f, 0f, 0f); 143 | double projection = Math.Abs(normal.Dot(global)); // absolute value so only need to check larger than 0.999 144 | if (projection >= 0.999) 145 | { 146 | global = new Vector3(0f, 1.0f, 0f); 147 | } 148 | 149 | // we first project the global onto the plane normal 150 | // then subtract to get the component of global IN the plane which will be are local axis in the recipe definition 151 | double projectionMagnitude = normal.Dot(global); 152 | Vector3 globalOnNormal = projectionMagnitude * normal; 153 | Vector3 globalOnPlane = global - globalOnNormal; 154 | // normalize 155 | globalOnPlane.Normalize(); 156 | 157 | // cross product of globalOnPlane and normal give vector in plane, otrhogonal to globalOnPlane 158 | Vector3 globalOnPlaneNormal = normal.Cross(globalOnPlane); 159 | globalOnPlaneNormal.Normalize(); 160 | 161 | // offset origin so the recipe is centered around the origin 162 | // translate half recipeWidth in negative direction of globalOnPlane 163 | origin = origin - globalOnPlane * recipeSize / 2; 164 | 165 | // translate half recipeWidth in negative direction of globalOnPlaneNormal 166 | origin = origin - globalOnPlaneNormal * recipeSize / 2; 167 | 168 | // Translate half the thickness in negative normal direction 169 | origin = origin - normal * recipeThickness / 2; 170 | 171 | Point3d recipeOrigin = new Point3d(origin.x, origin.y, origin.z); 172 | Vector3d xDirection = new Vector3d(globalOnPlane.x, globalOnPlane.y, globalOnPlane.z); 173 | Vector3d yDirection = new Vector3d(globalOnPlaneNormal.x, globalOnPlaneNormal.y, globalOnPlaneNormal.z); 174 | 175 | NXOpen.Xform recipeXform = femPart.Xforms.CreateXform(recipeOrigin, xDirection, yDirection, NXOpen.SmartObject.UpdateOption.AfterModeling, 1.0); 176 | NXOpen.CartesianCoordinateSystem recipeCoordinateSystem = femPart.CoordinateSystems.CreateCoordinateSystem(recipeXform, NXOpen.SmartObject.UpdateOption.AfterModeling); 177 | 178 | NXOpen.Unit unitMilliMeter = (NXOpen.Unit)femPart.UnitCollection.FindObject("MilliMeter"); 179 | NXOpen.Expression expressionLength = femPart.Expressions.CreateSystemExpressionWithUnits(recipeSize.ToString(), unitMilliMeter); 180 | NXOpen.Expression expressionWidth = femPart.Expressions.CreateSystemExpressionWithUnits(recipeSize.ToString(), unitMilliMeter); 181 | NXOpen.Expression expressionHeight = femPart.Expressions.CreateSystemExpressionWithUnits(recipeThickness.ToString(), unitMilliMeter); 182 | 183 | // SelectionRecipe selectionRecipe = femPart.SelectionRecipes.CreateBoxBoundingVolumeRecipe(datumPlane.Feature.Name, recipeCoordinateSystem, expressionLength, expressionWidth, expressionHeight, entitytypes); 184 | // Previous line is for pre NX1847, below is for NX1847 and later 185 | NXOpen.CAE.SelRecipeBuilder selRecipeBuilder = femPart.SelectionRecipes.CreateSelRecipeBuilder(); 186 | selRecipeBuilder.AddBoxBoundingVolumeStrategy(recipeCoordinateSystem, expressionLength, expressionWidth, expressionHeight, entitytypes, NXOpen.CAE.SelRecipeBuilder.InputFilterType.EntireModel, null); 187 | selRecipeBuilder.RecipeName = datumPlane.Feature.Name; 188 | SelectionRecipe selectionRecipe = (SelectionRecipe)selRecipeBuilder.Commit(); 189 | selRecipeBuilder.Destroy(); 190 | 191 | return selectionRecipe; 192 | } 193 | 194 | /// 195 | /// This function searches the part for all datum planes with a name and returns them. 196 | /// Naming a datum plane is done by right-clicking on the plane in the GUI and selecting rename. 197 | /// 198 | /// The part for which to return the named datum planes. 199 | /// An array with the named datum planes. 200 | public static DatumPlane[] GetNamedDatumPlanes(Part cadPart) 201 | { 202 | // using a list to easily add items, turning it into an array before returning. 203 | List namedDatumPlanes = new List(); 204 | foreach (DisplayableObject item in cadPart.Datums.ToArray()) 205 | { 206 | if (item is DatumPlane) 207 | { 208 | if (((DatumPlane)item).Feature.Name != "") 209 | { 210 | namedDatumPlanes.Add((DatumPlane)item); 211 | } 212 | } 213 | } 214 | 215 | return namedDatumPlanes.ToArray(); 216 | } 217 | 218 | /// 219 | /// This function returns the associated cad part for a given FemPart. 220 | /// Will load the part if not loaded. 221 | /// It assumes that the FemPart has an associated cad part (is not an orphan mesh) 222 | /// 223 | /// The FemPart for which to return the associated cad part. 224 | /// The associated cad part. 225 | public static Part GetAssociatedCadPart(FemPart femPart) 226 | { 227 | Part associatedCadPart = femPart.AssociatedCadPart; 228 | PartLoadStatus loadStatus; 229 | if (associatedCadPart == null) 230 | { 231 | // "load" the part (right-click load under fem) 232 | associatedCadPart = (Part)theSession.Parts.Open(femPart.FullPathForAssociatedCadPart, out loadStatus); 233 | } 234 | 235 | return associatedCadPart; 236 | } 237 | } 238 | } -------------------------------------------------------------------------------- /Tools/Excel/README.md: -------------------------------------------------------------------------------- 1 | # Excel and NXOpen journals 2 | 3 | Quite often we need to read excel files as part of our NXOpen journals. This section contains some options to do so. 4 | 5 | The easiest way to interact with excel file is by the using Microsoft.Office.Interop.Excel namespace. However, this is only directly available in VB.NET. 6 | It also requires that the machine executing the journal has Excel installed. 7 | 8 | >NOTE: The reason that the Microsoft.Office.Interop.Excel namespace is automatically included in VB.NET and not in C# has to do with the different design decisions made by the two languages and their respective frameworks. 9 | Visual Basic .NET (VB.NET) was designed to provide a more straightforward and beginner-friendly syntax for working with the .NET Framework. As part of this goal, VB.NET includes a feature called "implicit namespace referencing" that automatically includes some commonly used namespaces, such as Microsoft.Office.Interop.Excel, without requiring additional using statements. 10 | 11 | ## Excel and NXOpen journals in C# 12 | 13 | C# journals cannot make use of the Microsoft.Office.Interop.Excel namespace. The reason for this is that the namespace is not loaded within the NXOpen environment this journal is executed in. To solution is to compile the code, which allows you to include additional references. 14 | >NOTE: For compiling NXOpen code you need an additional license. 15 | 16 | Luckily there are some workarounds for reading excel files in journals: 17 | - Use VB.NET scripts 18 | - Create NXOpen code in Excel (see separate section) 19 | - Export Excel data as csv/txt and read like any other file. 20 | - Using System.Data.Oledb. This is an alternative method. It uses SQL syntax for manipulating the data. Some example code can be found in ReadExcelOleDB.cs 21 | - Using late binding and reflection of Microsoft.Office.Interop.Excel. (see separate section) 22 | 23 | >Note: C# was designed to be a more powerful and flexible language that is better suited for advanced programming tasks. C# does not include implicit namespace referencing by default because it was deemed that such a feature could lead to confusion and could make it harder to manage complex namespace hierarchies. As a result, C# requires you to explicitly include namespaces using using statements in order to use classes and methods from those namespaces, including Microsoft.Office.Interop.Excel. This makes the code more verbose, but also more explicit and easier to read and understand, especially in larger projects with many namespaces and classes. 24 | 25 | ### Create NXOpen code in Excel 26 | 27 | Very often data is only read from Excel to be used as paramters in NXOpen methods. So instead of reading the excel file and then using that data in NXOpen methods, one can also create the NXOpen code in Excel by string contatenation and then copy that code into the journal. 28 | 29 | Section 8, Lecture 41 of the [SimCenter 3D basic NXOpen course (C#)](https://www.udemy.com/course/simcenter3d-basic-nxopen-course/?referralCode=4ABC27CFD7D2C57D220B%20) shows this approach in more detail. 30 | 31 | 32 | ### Use late binding and reflection 33 | 34 | Using late binding and reflection, one can load a .dll at runtime and use the code in there. By loading Microsoft.Office.Interop.Excel.dll at runtime, we can access the code even though it is not loaded in the NXOpen runtime environment. All functionality from Microsoft.Office.Interop.Excel is available. 35 | 36 | There is a downside off course. Invoking the code is not that straight forward and intellisense does not work. ReadExcelReflection.cs contains some example code on how to read data from an Excel file. A class NXOpenExcel has been added to simplify the code. The same example, without using the NXOpenExcel class is also added as commented code for reference. 37 | 38 | ## Executing Excel and NXOpen journal on non-Windows machines 39 | 40 | If journals need to execute on non-Windows machines, Microsoft.Office.Interop.Excel is no longer an option since it requires an Excel installation. For these cases one can use [EPPlus](https://www.epplussoftware.com/). Versions 5 and above require a license, but older versions are free to use. For journals one can again use late binding. 41 | 42 | Switching to Python can also be an option. Then you can use openpyxl. 43 | >NOTE: Openpyxl can only read and write Excel FILES, it will not recalculate after changes. -------------------------------------------------------------------------------- /Tools/Excel/ReadExcel.cs: -------------------------------------------------------------------------------- 1 | // This is the default way to read and write excel files. 2 | // Note that it requires that Excel in installed on the machine you run this script on. 3 | // The downside is that you cannot run this as a journal, since you need to add the Microsoft.Office.Interop.Excel package in C# 4 | 5 | // Code taken from: https://stackoverflow.com/questions/657131/how-to-read-data-of-an-excel-file-using-c 6 | 7 | // untested 8 | namespace TheScriptingEngineer 9 | { 10 | using System; 11 | using System.IO; // for path operations 12 | using System.Collections.Generic; // for lists 13 | using NXOpen; // so we can use NXOpen functionality 14 | using NXOpen.CAE; // so we don't need to start everything with NXOpen.CAE 15 | using NXOpenUI; 16 | using NXOpen.UF; 17 | using NXOpen.Utilities; 18 | 19 | using Excel = Microsoft.Office.Interop.Excel; 20 | 21 | public class ReadExcel 22 | { 23 | // global variables used throughout 24 | public static Session theSession = Session.GetSession(); 25 | public static ListingWindow theLW = theSession.ListingWindow; 26 | public static BasePart basePart = theSession.Parts.BaseWork; 27 | 28 | public static void Main(string[] args) 29 | { 30 | // entrypoint for NX 31 | theLW.Open(); 32 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 33 | 34 | // Get the file to open 35 | string fileName = args[0]; // @"C:\temp\Sample1.xlsx"; 36 | 37 | // The classic way using Microsoft.Office.Interop.Excel 38 | Excel.Application xlApp = new Excel.Application(); 39 | Excel.Workbooks xlWorkbooks = xlApp.Workbooks; 40 | xlApp.Visible = true; 41 | Excel.Workbook xlWorkbook = xlWorkbooks.Open(fileName); 42 | Excel._Worksheet xlWorksheet = (Excel._Worksheet)xlWorkbook.Sheets[1]; 43 | Excel.Range xlRange = xlWorksheet.UsedRange; 44 | Console.WriteLine(xlRange.Cells.Rows.Count); 45 | Console.WriteLine(xlRange.Cells.Columns.Count); 46 | for (int i = 1; i <= xlRange.Cells.Rows.Count; i++) 47 | { 48 | //Excel.Range cell = xlWorksheet.Cells[i, 1] as Excel.Range; 49 | Excel.Range cell = xlWorksheet.Range["A" + i.ToString(), System.Type.Missing]; 50 | double value2 = (double)cell.Value2; 51 | Console.WriteLine(value2); 52 | } 53 | xlApp.Quit(); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /Tools/Excel/ReadExcelOleDb.cs: -------------------------------------------------------------------------------- 1 | // using this method you don't need to have Excel installed on the machine you are running this journal on. 2 | // Note that you do need to install the redistributible. 3 | // NOTE: You can only read and write Excel FILES, but CANNOT run Excel calculations (eg. when you change a value, the formulas will NOT update). 4 | 5 | // you need to download and install the Microsoft Access Database Engine 2010 Redistributable 6 | // on the machine where you are running the journal: http://www.microsoft.com/en-us/download/details.aspx?id=13255 7 | 8 | // Code copied from: 9 | // https://qawithexperts.com/article/c-sharp/read-excel-file-in-c-console-application-example-using-oledb/168 10 | // this still produces an error: The 'Microsoft.Jet.OLEDB.4.0' provider is not registered on the local machine. 11 | // found the solution to the error on following links (change from Microsoft.Jet.OLEDB.4.0 to Microsoft.ACE.OLEDB.12.0) 12 | // https://stackoverflow.com/questions/1991643/microsoft-jet-oledb-4-0-provider-is-not-registered-on-the-local-machine 13 | // Produces an error Microsoft.ACE.OLEDB.12.0' provider is not registered on the local machine (install Microsoft Access Database Engine 2010 Redistributable) 14 | // https://stackoverflow.com/questions/6649363/microsoft-ace-oledb-12-0-provider-is-not-registered-on-the-local-machine 15 | 16 | // For writing: 17 | // https://stackoverflow.com/questions/19662937/write-data-to-excel-using-oledb 18 | 19 | // Tested and working in SimCenter version 2023 release 2022.1 20 | 21 | namespace TheScriptingEngineer 22 | { 23 | using System; 24 | using System.Data; 25 | using System.Data.OleDb; // dotnet add package System.Data.OleDb 26 | using System.IO; // for path operations 27 | using System.Collections.Generic; // for lists 28 | using NXOpen; // so we can use NXOpen functionality 29 | using NXOpen.CAE; // so we don't need to start everything with NXOpen.CAE 30 | using NXOpenUI; 31 | using NXOpen.UF; 32 | using NXOpen.Utilities; 33 | 34 | public class ReadExcelOleDb 35 | { 36 | // global variables used throughout 37 | public static Session theSession = Session.GetSession(); 38 | public static ListingWindow theLW = theSession.ListingWindow; 39 | public static BasePart basePart = theSession.Parts.BaseWork; 40 | 41 | public static void Main(string[] args) 42 | { 43 | // entrypoint for NX 44 | theLW.Open(); 45 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 46 | 47 | string fileName = args[0]; // @"C:\temp\Sample1.xlsx"; 48 | // this is the connection string which has OLDB 12 Connection and Source URL of file 49 | // use HDR=YES if first excel row contains headers, HDR=NO means your excel's first row is not headers and it's data. 50 | // set IMEX=3;READONLY=FALSE for writing (so that SQL Insert and Update can be used) 51 | string connString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + fileName + "; Extended Properties='Excel 8.0;HDR=NO;IMEX=1;'"; 52 | theLW.WriteFullline("Using database connection string: " + connString); 53 | 54 | // Create the connection object 55 | System.Data.OleDb.OleDbConnection oledbConn = new OleDbConnection(connString); 56 | try 57 | { 58 | // Open connection 59 | oledbConn.Open(); 60 | 61 | // Create OleDbCommand object and select data from worksheet Sample-spreadsheet-file 62 | // here sheet name is Sheet1, usually it is Sheet1, Sheet2 etc.. 63 | OleDbCommand cmd = new OleDbCommand("SELECT * FROM [Sheet1$]", oledbConn); 64 | 65 | // Create new OleDbDataAdapter 66 | OleDbDataAdapter oleda = new OleDbDataAdapter(); 67 | 68 | oleda.SelectCommand = cmd; 69 | 70 | // Create a DataSet which will hold the data extracted from the worksheet. 71 | DataSet ds = new DataSet(); 72 | 73 | // Fill the DataSet from the data extracted from the worksheet. 74 | oleda.Fill(ds, "DataTable"); 75 | 76 | //loop through each row 77 | foreach(DataRowView item in ds.Tables[0].DefaultView) 78 | { 79 | theLW.WriteFullline(item.Row.ItemArray[0] +" "+item.Row.ItemArray[1] +" "+item.Row.ItemArray[2]); 80 | } 81 | } 82 | catch (Exception e) 83 | { 84 | theLW.WriteFullline("Error :" + e.Message); 85 | } 86 | finally 87 | { 88 | // Close connection 89 | oledbConn.Close(); 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Tools/Excel/ReadExcelReflection.cs: -------------------------------------------------------------------------------- 1 | // Read Excel data using late binding. 2 | // This allows you to read Excel data in a journal, without the need for compiling. 3 | // Note that it requires that Excel in installed on the machine you run this script on. 4 | 5 | // The main contains 2 blocks of code. 6 | // The first block uses a class NXOpenExcel which exposes some basic funtionality to open excel files and read data from it. 7 | // The second block shows how to 8 | 9 | // untested 10 | namespace TheScriptingEngineer 11 | { 12 | using System; 13 | using System.IO; // for path operations 14 | using System.Collections.Generic; // for lists 15 | using System.Reflection; // for using late binding. This namespace is located in mscorelib.dll and thus available in journaling. 16 | using NXOpen; // so we can use NXOpen functionality 17 | using NXOpen.CAE; // so we don't need to start everything with NXOpen.CAE 18 | using NXOpenUI; 19 | using NXOpen.UF; 20 | using NXOpen.Utilities; 21 | 22 | public class ReadExcelLateBinding 23 | { 24 | // global variables used throughout 25 | public static Session theSession = Session.GetSession(); 26 | public static ListingWindow theLW = theSession.ListingWindow; 27 | public static BasePart basePart = theSession.Parts.BaseWork; 28 | 29 | public static void Main(string[] args) 30 | { 31 | // entrypoint for NX 32 | theLW.Open(); 33 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 34 | 35 | // Get the file to open 36 | string fileName = args[0]; // @"C:\temp\Sample1.xlsx"; 37 | 38 | // using a wrapper class NXOpenExcel to make life easier 39 | NXOpenExcel xlApp = new NXOpenExcel(); 40 | // This is the path to Microsoft.Office.Interop.Excel.dll 41 | // It is typically located somewhere buried under C:\WINDOWS\assembly\ 42 | // you could keep a copy on a shared folder, so everyone using this script does not have to look for it on his or her machine. 43 | xlApp.Init(@"C:\WINDOWS\assembly\GAC_MSIL\Microsoft.Office.Interop.Excel\15.0.0.0__71e9bce111e9429c\Microsoft.Office.Interop.Excel.dll"); 44 | try 45 | { 46 | // xlApp.SetVisible(); // uncomment if you want to see what happens. 47 | object xlWorkbook = xlApp.OpenWorkbook(fileName); 48 | object xlWorksheet = xlApp.GetSheet(xlWorkbook, 1); 49 | int lastRow = xlApp.GetLastRow(xlWorksheet); 50 | int lastColumn = xlApp.GetLastColumn(xlWorksheet); 51 | theLW.WriteFullline("Cell[A1] = " + xlApp.GetCellValueA1Notation(xlWorksheet, "A1").ToString()); 52 | 53 | for (int i = 1; i <= lastRow; i++) 54 | { 55 | for (int j = 1; j <= lastColumn; j++) 56 | { 57 | object value2 = xlApp.GetCellValueIndex(xlWorksheet, i, j); 58 | theLW.WriteFullline(value2.ToString()); 59 | } 60 | } 61 | } 62 | catch (System.Exception) 63 | { 64 | throw; 65 | } 66 | finally 67 | { 68 | xlApp.Quit(); 69 | } 70 | 71 | // // Using reflection, without a wrapper class. 72 | // // This gives you assess to all Interop.Excel functionality 73 | // // This is the path to Microsoft.Office.Interop.Excel.dll 74 | // // It is typically located somewhere buried under C:\WINDOWS\assembly\ 75 | // // you could keep a copy on a shared folder, so everyone using this script does not have to look for it on his or her machine. 76 | // System.Reflection.Assembly excelInteropDll = System.Reflection.Assembly.LoadFile(@"C:\WINDOWS\assembly\GAC_MSIL\Microsoft.Office.Interop.Excel\15.0.0.0__71e9bce111e9429c\Microsoft.Office.Interop.Excel.dll"); 77 | // System.Type xlAppType = excelInteropDll.GetType("Microsoft.Office.Interop.Excel.ApplicationClass"); 78 | // object xlApp = Activator.CreateInstance(xlAppType); 79 | // try 80 | // { 81 | // xlApp.GetType().InvokeMember("Visible", BindingFlags.Public | BindingFlags.SetProperty | BindingFlags.Instance, null, xlApp, new object[] { true }); 82 | // object xlWorkbooks = xlApp.GetType().InvokeMember("Workbooks", BindingFlags.GetProperty, null, xlApp, null); 83 | // object xlWorkbook = xlWorkbooks.GetType().InvokeMember("Open", BindingFlags.InvokeMethod, null, xlWorkbooks, new object[] { @"C:\temp\ExcelReflection\test.xlsx" }); 84 | // object xlWorksheets = xlWorkbook.GetType().InvokeMember("Worksheets", BindingFlags.GetProperty, null, xlWorkbook, null); 85 | // object xlWorksheet = xlWorksheets.GetType().InvokeMember("Item", BindingFlags.GetProperty, null, xlWorksheets, new object[] { 1 }); 86 | // object xlUsedRange = xlWorksheet.GetType().InvokeMember("UsedRange", BindingFlags.GetProperty, null, xlWorksheet, null); 87 | // object xlCells = xlUsedRange.GetType().InvokeMember("Cells", BindingFlags.GetProperty, null, xlUsedRange, null); 88 | // object xlRows = xlCells.GetType().InvokeMember("Rows", BindingFlags.GetProperty, null, xlCells, null); 89 | // object xlColumns = xlCells.GetType().InvokeMember("Columns", BindingFlags.GetProperty, null, xlCells, null); 90 | // int xlRowsCount = (int)xlRows.GetType().InvokeMember("Count", BindingFlags.GetProperty, null, xlRows, null); 91 | // int xlColumnsCount = (int)xlRows.GetType().InvokeMember("Count", BindingFlags.GetProperty, null, xlColumns, null); 92 | // string[] reference = new string[] { "A1" }; 93 | // object rangeA1 = xlWorksheet.GetType().InvokeMember("Range", BindingFlags.GetProperty, null, xlWorksheet, reference); 94 | // object value2A1 = rangeA1.GetType().InvokeMember("Value2", BindingFlags.GetProperty, null, rangeA1, null); 95 | // theLW.WriteFullline("Cell[A1] = " + value2A1.ToString()); 96 | // for (int i = 1; i <= xlRowsCount; i++) 97 | // { 98 | // for (int j = 1; j <= xlColumnsCount; j++) 99 | // { 100 | // object[] id = new object[] { i, j}; 101 | // object range = xlWorksheet.GetType().InvokeMember("Cells", BindingFlags.GetProperty, null, xlWorksheet, id); 102 | // object value2 = range.GetType().InvokeMember("Value2", BindingFlags.GetProperty, null, range, null); 103 | // theLW.WriteFullline(value2.ToString()); 104 | // } 105 | // } 106 | // } 107 | // catch (System.Exception) 108 | // { 109 | 110 | // throw; 111 | // } 112 | // finally 113 | // { 114 | // xlApp.GetType().InvokeMember("Quit", BindingFlags.InvokeMethod, null, xlApp, null); 115 | // } 116 | } 117 | } 118 | 119 | public class NXOpenExcel 120 | { 121 | object excelInstance { get; set; } 122 | 123 | public void Init(string filePath) 124 | { 125 | // Load the Microsoft.Office.Interop.Excel assembly at runtime 126 | Assembly officeInteropAssembly = Assembly.LoadFrom(filePath); 127 | 128 | // Get the Excel.Application type from the assembly 129 | System.Type excelType = officeInteropAssembly.GetType("Microsoft.Office.Interop.Excel.ApplicationClass"); 130 | 131 | if (excelType != null) 132 | { 133 | excelInstance = Activator.CreateInstance(excelType); 134 | } 135 | else 136 | { 137 | Console.WriteLine("Excel is not installed on this machine."); 138 | excelInstance = null; 139 | } 140 | } 141 | 142 | 143 | public void Quit() 144 | { 145 | excelInstance.GetType().InvokeMember("Quit", BindingFlags.InvokeMethod, null, excelInstance, null); 146 | excelInstance = null; 147 | } 148 | 149 | 150 | public void SetVisible() 151 | { 152 | excelInstance.GetType().InvokeMember("Visible", BindingFlags.SetProperty, null, excelInstance, new object[] { true }); 153 | } 154 | 155 | 156 | public object OpenWorkbook(string filePath) 157 | { 158 | object workbooks = excelInstance.GetType().InvokeMember("Workbooks", BindingFlags.GetProperty, null, excelInstance, null); 159 | object workbook = workbooks.GetType().InvokeMember("Open", BindingFlags.InvokeMethod, null, workbooks, new object[] { filePath }); 160 | return workbook; 161 | } 162 | 163 | 164 | public void CloseWorkbook(object workbook) 165 | { 166 | excelInstance.GetType().InvokeMember("Close", BindingFlags.InvokeMethod, null, workbook, new object[] { false }); 167 | } 168 | 169 | 170 | public object GetSheet(object workbook, int index) 171 | { 172 | // Get a reference to a worksheet 173 | object worksheets = workbook.GetType().InvokeMember("Worksheets", BindingFlags.GetProperty, null, workbook, null); 174 | object worksheet = worksheets.GetType().InvokeMember("Item", BindingFlags.GetProperty, null, worksheets, new object[] { index }); 175 | return worksheet; 176 | } 177 | 178 | 179 | public int GetLastRow(object worksheet) 180 | { 181 | // Get the last used row in the worksheet 182 | object usedRange = worksheet.GetType().InvokeMember("UsedRange", BindingFlags.GetProperty, null, worksheet, null); 183 | object cells = usedRange.GetType().InvokeMember("Cells", BindingFlags.GetProperty, null, usedRange, null); 184 | object rows = cells.GetType().InvokeMember("Rows", BindingFlags.GetProperty, null, cells, null); 185 | int lastRow = (int)rows.GetType().InvokeMember("Count", BindingFlags.GetProperty, null, rows, null); 186 | 187 | return lastRow; 188 | } 189 | 190 | public int GetLastColumn(object worksheet) 191 | { 192 | // Get the last used row in the worksheet 193 | object usedRange = worksheet.GetType().InvokeMember("UsedRange", BindingFlags.GetProperty, null, worksheet, null); 194 | object cells = usedRange.GetType().InvokeMember("Cells", BindingFlags.GetProperty, null, usedRange, null); 195 | object columns = cells.GetType().InvokeMember("Columns", BindingFlags.GetProperty, null, cells, null); 196 | int lastColumn = (int)columns.GetType().InvokeMember("Count", BindingFlags.GetProperty, null, columns, null); 197 | 198 | return lastColumn; 199 | } 200 | 201 | public object GetCellValueA1Notation(object worksheet, string A1Notation) 202 | { 203 | // Get the value of a single cell 204 | string[] id = new string[] { A1Notation }; 205 | object range = worksheet.GetType().InvokeMember("Range", BindingFlags.GetProperty, null, worksheet, id); 206 | object value2 = range.GetType().InvokeMember("Value2", BindingFlags.GetProperty, null, range, null); 207 | return value2; 208 | } 209 | 210 | public object GetCellValueIndex(object worksheet, int rowIndex, int columnIndex) 211 | { 212 | // Get the value of a single cell 213 | object[] id = new object[] { rowIndex, columnIndex}; 214 | object range = worksheet.GetType().InvokeMember("Cells", BindingFlags.GetProperty, null, worksheet, id); 215 | object value2 = range.GetType().InvokeMember("Value2", BindingFlags.GetProperty, null, range, null); 216 | return value2; 217 | } 218 | 219 | public string GetColumnName(int columnNumber) 220 | { 221 | int dividend = columnNumber; 222 | string columnName = String.Empty; 223 | int modulo; 224 | 225 | while (dividend > 0) 226 | { 227 | modulo = (dividend - 1) % 26; 228 | columnName = Convert.ToChar(65 + modulo).ToString() + columnName; 229 | dividend = (int)((dividend - modulo) / 26); 230 | } 231 | 232 | return columnName; 233 | } 234 | 235 | public int GetColumnNumber(string columnName) 236 | { 237 | int columnNumber = 0; 238 | int factor = 1; 239 | 240 | for (int i = columnName.Length - 1; i >= 0; i--) 241 | { 242 | char letter = columnName[i]; 243 | int value = letter - 'A' + 1; 244 | 245 | columnNumber += value * factor; 246 | factor *= 26; 247 | } 248 | 249 | return columnNumber; 250 | } 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /Tools/Excel/Sample1.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theScriptingEngineer/NXOpen-CAE/c35f3386f412d551f0527899f03e4ecca8542e81/Tools/Excel/Sample1.xlsx -------------------------------------------------------------------------------- /Tools/PointsToCsv.cs: -------------------------------------------------------------------------------- 1 | namespace TheScriptingEngineer 2 | { 3 | using System; 4 | using System.IO; // for path operations 5 | using System.Collections.Generic; // for lists 6 | using NXOpen; // so we can use NXOpen functionality 7 | using NXOpen.CAE; // so we don't need to start everything with NXOpen.CAE 8 | using NXOpenUI; 9 | using NXOpen.UF; 10 | using NXOpen.Utilities; 11 | 12 | public class PointsToCsv 13 | { 14 | // global variables used throughout 15 | public static Session theSession = Session.GetSession(); 16 | public static ListingWindow theLW = theSession.ListingWindow; 17 | public static BasePart basePart = theSession.Parts.BaseWork; 18 | 19 | public static void Main(string[] args) 20 | { 21 | // entrypoint for NX 22 | theLW.Open(); 23 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 24 | 25 | Point[] allPoints = GetAllPoints(basePart); 26 | // List all points in the listing window 27 | ListCsv(allPoints); 28 | // Write points to csv file 29 | WriteCsv(@"C:\myPoints.csv", allPoints); 30 | } 31 | 32 | /// 33 | /// This function returns all points in a part. 34 | /// 35 | /// The part for which to to return the points 36 | /// An array with all points in the part. 37 | public static Point[] GetAllPoints(BasePart basePart) 38 | { 39 | Point[] allPoints = basePart.Points.ToArray(); 40 | 41 | return allPoints; 42 | } 43 | 44 | /// 45 | /// This function writes the coordinates of all points to a .csv file. 46 | /// 47 | /// The file name and path for the csv file. If no path provided, it is written to the location of the part. 48 | /// The array of points to write the coordinates to file. 49 | public static void WriteCsv(string fileName, Point[] points) 50 | { 51 | string FullPath = CreateFullPath(fileName, ".csv"); 52 | string fileContent = ""; 53 | foreach (Point item in points) 54 | { 55 | fileContent = fileContent + item.Coordinates.X.ToString().Replace(",",".") + "," + item.Coordinates.Y.ToString().Replace(",",".") + "," + item.Coordinates.Z.ToString().Replace(",",".") + Environment.NewLine; 56 | } 57 | 58 | File.WriteAllText(FullPath, fileContent); 59 | } 60 | 61 | /// 62 | /// This function lists the coordinates of all points to the listing window. 63 | /// 64 | /// The array of points to list the coordinates for. 65 | public static void ListCsv(Point[] points) 66 | { 67 | foreach (Point item in points) 68 | { 69 | theLW.WriteFullline(item.Coordinates.X.ToString().Replace(",",".") + "," + item.Coordinates.Y.ToString().Replace(",",".") + "," + item.Coordinates.Z.ToString().Replace(",",".")); 70 | } 71 | } 72 | 73 | /// 74 | /// This function takes a filename and adds an extension and path of the part if not provided by the user. 75 | /// If the fileName contains an extension, this function leaves it untouched, othwerwise adds the provided extension, which defaults to .unv. 76 | /// If the fileName contains a path, this function leaves it untouched, otherwise adds the path of the BasePart as the path. 77 | /// 78 | /// The filename with or without path and .unv extension. 79 | /// Optional: The extension to add if missing. Defaults to .unv. 80 | /// A string with extension and path of basePart if the fileName parameter did not include a path. 81 | public static string CreateFullPath(string fileName, string extension = ".unv") 82 | { 83 | // check if .unv is included in fileName 84 | if (Path.GetExtension(fileName).Length == 0) 85 | { 86 | fileName = fileName + extension; 87 | } 88 | 89 | // check if path is included in fileName, if not add path of the .sim file 90 | string unvFilePath = Path.GetDirectoryName(fileName); 91 | if (unvFilePath == "") 92 | { 93 | // if the basePart file has never been saved, the next will give an error 94 | fileName = Path.Combine(Path.GetDirectoryName(basePart.FullPath), fileName); 95 | } 96 | 97 | return fileName; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Tools/PrintAssemblyStructure.cs: -------------------------------------------------------------------------------- 1 | // answer to https://community.sw.siemens.com/s/question/0D54O00007a5m2sSAA/how-can-i-find-a-components-owning-assembly-in-nxopen 2 | // answer to https://community.sw.siemens.com/s/question/0D54O00007bOTKnSAO/how-to-get-component-in-main-assembly-to-the-sub-assembly-level 3 | namespace TheScriptingEngineer 4 | { 5 | using System; 6 | using NXOpen; 7 | using NXOpen.CAE; // so we don't need to start everything with NXOpen.CAE 8 | 9 | public class PrintAssemblyStructure 10 | { 11 | static NXOpen.Session theSession = NXOpen.Session.GetSession(); 12 | static ListingWindow theLW = theSession.ListingWindow; 13 | 14 | public static void Main(string[] args) 15 | { 16 | theLW.Open(); 17 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 18 | 19 | BasePart[] allPartsInSession = theSession.Parts.ToArray(); 20 | theLW.WriteFullline("The following parts are loaded in the session: "); 21 | foreach (BasePart item in allPartsInSession) 22 | { 23 | theLW.WriteFullline(string.Format("\t{0, -50}{1, -128}", item.Name, item.FullPath)); 24 | } 25 | theLW.WriteFullline(""); 26 | 27 | BasePart basePart = theSession.Parts.BaseWork; 28 | BasePart baseDisplayPart = theSession.Parts.BaseDisplay; 29 | theLW.WriteFullline("The current workpart is: " + basePart.Name + " located in " + basePart.FullPath); 30 | theLW.WriteFullline("The current displaypart is: " + baseDisplayPart.Name + " located in " + baseDisplayPart.FullPath); 31 | theLW.WriteFullline(""); 32 | 33 | PrintComponentTree(basePart.ComponentAssembly.RootComponent); 34 | PrintPartTree(basePart); 35 | } 36 | 37 | /// 38 | /// Prints the component tree for the given component to the listing window. 39 | /// 40 | /// The component for whch to print the component tree 41 | /// Optional parameter used for creating indentations. 42 | public static void PrintComponentTree(NXOpen.Assemblies.Component component, int requestedLevel = 0) 43 | { 44 | int level = requestedLevel; 45 | theLW.WriteFullline(Indentation(level) + "| " + component.JournalIdentifier + " is a compont(instance) of " + component.Prototype.OwningPart.Name + " located in " + component.OwningPart.Name); 46 | NXOpen.Assemblies.Component[] children = component.GetChildren(); 47 | for (int i = children.Length - 1; i >= 0 ; i--) 48 | { 49 | PrintComponentTree(children[i], level + 1); 50 | } 51 | } 52 | 53 | /// 54 | /// Prints the part tree for the given BasePart to the listing window. 55 | /// 56 | /// The BasePart to print the tree for. 57 | /// Optional parameter used for creating indentations. 58 | public static void PrintPartTree(BasePart basePart, int requestedLevel = 0) 59 | { 60 | int level = requestedLevel; 61 | if (basePart as SimPart != null) 62 | { 63 | SimPart simPart = (SimPart)basePart; 64 | theLW.WriteFullline(simPart.Name); 65 | 66 | // PrintPartTree(simPart.ComponentAssembly.RootComponent.GetChildren()[0].Prototype.OwningPart); 67 | PrintPartTree(simPart.FemPart); 68 | } 69 | else if (basePart as AssyFemPart != null) 70 | { 71 | AssyFemPart assyFemPart = (AssyFemPart)basePart; 72 | theLW.WriteFullline(Indentation(level) + "| " + assyFemPart.Name + " located in " + assyFemPart.FullPath + " linked to part " + assyFemPart.FullPathForAssociatedCadPart); 73 | NXOpen.Assemblies.Component[] children = assyFemPart.ComponentAssembly.RootComponent.GetChildren(); 74 | for (int i = 0; i < children.Length; i++) 75 | { 76 | PrintPartTree(children[i].Prototype.OwningPart, level + 1); 77 | } 78 | } 79 | else if (basePart as FemPart != null) 80 | { 81 | FemPart femPart = (FemPart)basePart; 82 | // try catch since calling femPart.FullPathForAssociatedCadPart on a part which has no cad part results in an error 83 | try 84 | { 85 | // femPart.MasterCadPart returns the actual part, but is null if the part is not loaded. 86 | theLW.WriteFullline(Indentation(level) + "| " + femPart.Name + " which is linked to part " + femPart.FullPathForAssociatedCadPart); 87 | } 88 | catch (System.Exception) 89 | { 90 | // femPart has no associated cad part 91 | theLW.WriteFullline(Indentation(level) + "| " + femPart.Name + " not linked to a part."); 92 | } 93 | } 94 | else 95 | { 96 | theLW.WriteFullline(Indentation(level) + "| " + basePart.Name + " located in " + basePart.FullPath); 97 | NXOpen.Assemblies.Component[] children = basePart.ComponentAssembly.RootComponent.GetChildren(); 98 | for (int i = 0; i < children.Length; i++) 99 | { 100 | PrintPartTree(children[i].Prototype.OwningPart, level + 1); 101 | } 102 | } 103 | 104 | } 105 | 106 | /// 107 | /// Helper method to create indentations (eg tabs) with a given length. 108 | /// 109 | /// The depth of the indentations. 110 | /// A string with the indentations. 111 | public static string Indentation(int level) 112 | { 113 | string indentation = ""; 114 | for (int i = 0; i < level + 1; i++) 115 | { 116 | indentation = indentation + "\t"; 117 | } 118 | 119 | return indentation; 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /Tools/VectorArithmetic.cs: -------------------------------------------------------------------------------- 1 | namespace TheScriptingEngineer 2 | { 3 | using System; 4 | using System.IO; // for path operations 5 | using System.Collections.Generic; // for lists 6 | using NXOpen; 7 | using NXOpen.CAE; 8 | using NXOpenUI; 9 | using NXOpen.UF; 10 | using NXOpen.Utilities; 11 | using NXOpen.VectorArithmetic; 12 | 13 | public class VectorArithmetic 14 | { 15 | // global variables used throughout 16 | public static Session theSession = Session.GetSession(); 17 | public static ListingWindow theLW = theSession.ListingWindow; 18 | public static CaePart baseCAEPart = (CaePart)theSession.Parts.BaseWork; 19 | 20 | public static void Main(string[] args) 21 | { 22 | // entrypoint for NX 23 | theLW.Open(); 24 | theLW.WriteFullline("Starting Main() in " + theSession.ExecutingJournal); 25 | 26 | Vector3 test = new Vector3(1.0f, 2.0f, 3.0f); 27 | Vector3 sum = test + test; 28 | theLW.WriteFullline(sum.ToString()); 29 | 30 | Vector3 globalX = new Vector3(1.0f, 0f, 0f); 31 | Vector3 globalY = new Vector3(0f, 1.0f, 0f); 32 | Vector3 globalZ = new Vector3(0f, 0f, 1.0f); 33 | 34 | theLW.WriteFullline("Some examples of vector arithmetic using NXOpen.VectorArithmetic:"); 35 | theLW.WriteFullline("Using the following vectors it the examples:"); 36 | theLW.WriteFullline("Test: " + PrintVector3(test)); 37 | theLW.WriteFullline("GlobalX: " + PrintVector3(globalX)); 38 | theLW.WriteFullline("GlobalY: " + PrintVector3(globalY)); 39 | theLW.WriteFullline("GlobalZ: " + PrintVector3(globalZ)); 40 | theLW.WriteFullline(""); 41 | 42 | theLW.WriteFullline("Test + GlobalX = " + PrintVector3(test + globalX)); 43 | theLW.WriteFullline("Test - GlobalX = " + PrintVector3(test - globalX)); 44 | theLW.WriteFullline("Test x 2 = " + PrintVector3(test * 2)); 45 | theLW.WriteFullline("GlobalZ x 2 = " + PrintVector3(globalZ * 2)); 46 | theLW.WriteFullline("Test - GlobalX - 2 x GlobalY - 3 x GlobalZ = " + PrintVector3(test - globalX - 2 * globalY - 3 * globalZ)); 47 | theLW.WriteFullline(""); 48 | 49 | theLW.WriteFullline("Dot product of test and globalX = " + test.Dot(globalX).ToString()); 50 | theLW.WriteFullline("Dot product of test and globalY = " + test.Dot(globalY).ToString()); 51 | theLW.WriteFullline("Dot product of test and globalZ = " + test.Dot(globalZ).ToString()); 52 | theLW.WriteFullline(""); 53 | 54 | theLW.WriteFullline("Cross product of test and globalX = " + PrintVector3(test.Cross(globalX))); 55 | theLW.WriteFullline("Cross product of globalX and globalY = " + PrintVector3(globalX.Cross(globalY))); 56 | theLW.WriteFullline(""); 57 | 58 | test.Normalize(); 59 | theLW.WriteFullline("Test normalized = " + PrintVector3(test)); 60 | } 61 | 62 | /// 63 | /// This function takes a NXOpen.VectorArithmetic.Vector3 and creates a string representation. 64 | /// Required because there is not overridden ToString() method in the class. 65 | /// 66 | /// The vector to represent as string. 67 | /// A string representation of a NXOpen.VectorArithmetic.Vector3 68 | public static string PrintVector3(Vector3 vector) 69 | { 70 | return "(" + vector.x.ToString() + ", " + vector.y.ToString() + ", " + vector.z.ToString() + ")"; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 theScriptingEngineer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | --------------------------------------------------------------------------------