├── .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 |
--------------------------------------------------------------------------------