diff --git a/.opt b/.opt deleted file mode 100644 index fa7aa42..0000000 --- a/.opt +++ /dev/null @@ -1,692 +0,0 @@ -General.AxesFormatX = "%.3g"; -General.AxesFormatY = "%.3g"; -General.AxesFormatZ = "%.3g"; -General.AxesLabelX = ""; -General.AxesLabelY = ""; -General.AxesLabelZ = ""; -General.BackgroundImageFileName = ""; -General.BuildInfo = "Version: 4.11.1; License: GNU General Public License; Build OS: Linux64-sdk; Build date: 20221221; Build host: gmsh.info; Build options: 64Bit ALGLIB[contrib] ANN[contrib] Bamg Blas[petsc] Blossom Cgns DIntegration Dlopen DomHex Eigen[contrib] Fltk Gmm[contrib] Hxt Jpeg Kbipack Lapack[petsc] LinuxJoystick MathEx[contrib] Med Mesh Metis[contrib] Mmg Mpeg Netgen ONELAB ONELABMetamodel OpenCASCADE OpenCASCADE-CAF OpenGL OpenMP OptHom PETSc Parser Plugins Png Post QuadMeshingTools QuadTri Solver TetGen/BR Voro++[contrib] WinslowUntangler Zlib; FLTK version: 1.4.0; PETSc version: 3.14.4 (real arithmtic); OCC version: 7.6.1; MED version: 4.1.0; Packaged by: geuzaine; Web site: https://gmsh.info; Issue tracker: https://gitlab.onelab.info/gmsh/gmsh/issues"; -General.BuildOptions = "64Bit ALGLIB[contrib] ANN[contrib] Bamg Blas[petsc] Blossom Cgns DIntegration Dlopen DomHex Eigen[contrib] Fltk Gmm[contrib] Hxt Jpeg Kbipack Lapack[petsc] LinuxJoystick MathEx[contrib] Med Mesh Metis[contrib] Mmg Mpeg Netgen ONELAB ONELABMetamodel OpenCASCADE OpenCASCADE-CAF OpenGL OpenMP OptHom PETSc Parser Plugins Png Post QuadMeshingTools QuadTri Solver TetGen/BR Voro++[contrib] WinslowUntangler Zlib"; -General.DefaultFileName = "untitled.geo"; -General.Display = ""; -General.ErrorFileName = ".gmsh-errors"; -General.ExecutableFileName = "/home/yuxiangpt/anaconda3/envs/amworkflow/bin/python3.10"; -General.FileName = ""; -General.FltkTheme = ""; -General.GraphicsFont = "Helvetica"; -General.GraphicsFontEngine = "Native"; -General.GraphicsFontTitle = "Helvetica"; -General.OptionsFileName = ".gmsh-options"; -General.RecentFile0 = "untitled.geo"; -General.RecentFile1 = "untitled.geo"; -General.RecentFile2 = "untitled.geo"; -General.RecentFile3 = "untitled.geo"; -General.RecentFile4 = "untitled.geo"; -General.RecentFile5 = "untitled.geo"; -General.RecentFile6 = "untitled.geo"; -General.RecentFile7 = "untitled.geo"; -General.RecentFile8 = "untitled.geo"; -General.RecentFile9 = "untitled.geo"; -General.ScriptingLanguages = "geo"; -General.TextEditor = "gedit '%s'"; -General.TmpFileName = ".gmsh-tmp"; -General.Version = "4.11.1"; -General.WatchFilePattern = ""; -General.AbortOnError = 2; -General.AlphaBlending = 1; -General.Antialiasing = 0; -General.ArrowHeadRadius = 0.12; -General.ArrowStemLength = 0.5600000000000001; -General.ArrowStemRadius = 0.02; -General.Axes = 0; -General.AxesMikado = 0; -General.AxesAutoPosition = 1; -General.AxesForceValue = 0; -General.AxesMaxX = 1; -General.AxesMaxY = 1; -General.AxesMaxZ = 1; -General.AxesMinX = 0; -General.AxesMinY = 0; -General.AxesMinZ = 0; -General.AxesTicsX = 5; -General.AxesTicsY = 5; -General.AxesTicsZ = 5; -General.AxesValueMaxX = 1; -General.AxesValueMaxY = 1; -General.AxesValueMaxZ = 1; -General.AxesValueMinX = 0; -General.AxesValueMinY = 0; -General.AxesValueMinZ = 0; -General.BackgroundGradient = 1; -General.BackgroundImage3D = 0; -General.BackgroundImagePage = 0; -General.BackgroundImagePositionX = 0; -General.BackgroundImagePositionY = 0; -General.BackgroundImageWidth = -1; -General.BackgroundImageHeight = -1; -General.BoundingBoxSize = 164.4466475101478; -General.Camera = 0; -General.CameraAperture = 40; -General.CameraEyeSeparationRatio = 1.5; -General.CameraFocalLengthRatio = 1; -General.Clip0A = 1; -General.Clip0B = 0; -General.Clip0C = 0; -General.Clip0D = 0; -General.Clip1A = 0; -General.Clip1B = 1; -General.Clip1C = 0; -General.Clip1D = 0; -General.Clip2A = 0; -General.Clip2B = 0; -General.Clip2C = 1; -General.Clip2D = 0; -General.Clip3A = -1; -General.Clip3B = 0; -General.Clip3C = 0; -General.Clip3D = 1; -General.Clip4A = 0; -General.Clip4B = -1; -General.Clip4C = 0; -General.Clip4D = 1; -General.Clip5A = 0; -General.Clip5B = 0; -General.Clip5C = -1; -General.Clip5D = 1; -General.ClipFactor = 5; -General.ClipOnlyDrawIntersectingVolume = 0; -General.ClipOnlyVolume = 0; -General.ClipPositionX = 656; -General.ClipPositionY = 177; -General.ClipWholeElements = 0; -General.ColorScheme = 1; -General.ConfirmOverwrite = 1; -General.ContextPositionX = 674; -General.ContextPositionY = 258; -General.DetachedMenu = 0; -General.DetachedProcess = 1; -General.DisplayBorderFactor = 0.2; -General.DoubleBuffer = 1; -General.DrawBoundingBoxes = 0; -General.ExpertMode = 0; -General.ExtraPositionX = 650; -General.ExtraPositionY = 350; -General.ExtraHeight = 100; -General.ExtraWidth = 100; -General.FastRedraw = 0; -General.FieldPositionX = 650; -General.FieldPositionY = 550; -General.FieldHeight = 344; -General.FieldWidth = 447; -General.FileChooserPositionX = 200; -General.FileChooserPositionY = 200; -General.FltkColorScheme = 0; -General.FltkRefreshRate = 5; -General.FontSize = -1; -General.GraphicsFontSize = 15; -General.GraphicsFontSizeTitle = 18; -General.GraphicsHeight = 967; -General.GraphicsPositionX = 1086; -General.GraphicsPositionY = 185; -General.GraphicsWidth = 1908; -General.HighOrderToolsPositionX = 650; -General.HighOrderToolsPositionY = 150; -General.HighResolutionGraphics = 1; -General.InitialModule = 0; -General.InputScrolling = 1; -General.Light0 = 1; -General.Light0X = 0.65; -General.Light0Y = 0.65; -General.Light0Z = 1; -General.Light0W = 0; -General.Light1 = 0; -General.Light1X = 0.5; -General.Light1Y = 0.3; -General.Light1Z = 1; -General.Light1W = 0; -General.Light2 = 0; -General.Light2X = 0.5; -General.Light2Y = 0.3; -General.Light2Z = 1; -General.Light2W = 0; -General.Light3 = 0; -General.Light3X = 0.5; -General.Light3Y = 0.3; -General.Light3Z = 1; -General.Light3W = 0; -General.Light4 = 0; -General.Light4X = 0.5; -General.Light4Y = 0.3; -General.Light4Z = 1; -General.Light4W = 0; -General.Light5 = 0; -General.Light5X = 0.5; -General.Light5Y = 0.3; -General.Light5Z = 1; -General.Light5W = 0; -General.LineWidth = 1; -General.ManipulatorPositionX = 650; -General.ManipulatorPositionY = 150; -General.MaxX = 104.6; -General.MaxY = 102.8596811935287; -General.MaxZ = 4.3000001; -General.MenuWidth = 352; -General.MenuHeight = 200; -General.MenuPositionX = 400; -General.MenuPositionY = 400; -General.MessageFontSize = -1; -General.MessageHeight = 300; -General.MinX = -15.92017603599525; -General.MinY = -8.939679465911187; -General.MinZ = -1e-07; -General.MouseHoverMeshes = 0; -General.MouseSelection = 1; -General.MouseInvertZoom = 0; -General.NativeFileChooser = 0; -General.NonModalWindows = 1; -General.NoPopup = 0; -General.NumThreads = 1; -General.OptionsPositionX = 650; -General.OptionsPositionY = 150; -General.Orthographic = 1; -General.PluginPositionX = 650; -General.PluginPositionY = 550; -General.PluginHeight = 344; -General.PluginWidth = 486; -General.PointSize = 3; -General.PolygonOffsetAlwaysOn = 0; -General.PolygonOffsetFactor = 1; -General.PolygonOffsetUnits = 1; -General.ProgressMeterStep = 10; -General.QuadricSubdivisions = 6; -General.RotationX = 292.3291507465278; -General.RotationY = 10.71264592661617; -General.RotationZ = 310.9971479735397; -General.RotationCenterGravity = 1; -General.RotationCenterX = 0; -General.RotationCenterY = 0; -General.RotationCenterZ = 0; -General.SaveOptions = 0; -General.SaveSession = 1; -General.ScaleX = 1.639660634628693; -General.ScaleY = 1.639660634628693; -General.ScaleZ = 1.639660634628693; -General.Shininess = 0.4; -General.ShininessExponent = 40; -General.ShowModuleMenu = 1; -General.ShowOptionsOnStartup = 0; -General.ShowMessagesOnStartup = 0; -General.SmallAxes = 1; -General.SmallAxesPositionX = -60; -General.SmallAxesPositionY = -40; -General.SmallAxesSize = 30; -General.StatisticsPositionX = 656; -General.StatisticsPositionY = 177; -General.Stereo = 0; -General.SystemMenuBar = 1; -General.Terminal = 1; -General.Tooltips = 1; -General.Trackball = 1; -General.TrackballHyperbolicSheet = 1; -General.TrackballQuaternion0 = -0.5366136910182068; -General.TrackballQuaternion1 = -0.1593519707688598; -General.TrackballQuaternion2 = -0.3902727991166964; -General.TrackballQuaternion3 = -0.7309855253652437; -General.TranslationX = 54.48688382056832; -General.TranslationY = -18.15102585856696; -General.TranslationZ = 0; -General.VectorType = 4; -General.Verbosity = 5; -General.VisibilityPositionX = 656; -General.VisibilityPositionY = 177; -General.ZoomFactor = 4; -General.Color.Background = {255,255,255}; -General.Color.BackgroundGradient = {208,215,255}; -General.Color.Foreground = {85,85,85}; -General.Color.Text = {0,0,0}; -General.Color.Axes = {0,0,0}; -General.Color.SmallAxes = {0,0,0}; -General.Color.AmbientLight = {25,25,25}; -General.Color.DiffuseLight = {255,255,255}; -General.Color.SpecularLight = {255,255,255}; -Geometry.DoubleClickedPointCommand = "ONELAB"; -Geometry.DoubleClickedCurveCommand = "ONELAB"; -Geometry.DoubleClickedSurfaceCommand = "ONELAB"; -Geometry.DoubleClickedVolumeCommand = "ONELAB"; -Geometry.OCCTargetUnit = ""; -Geometry.PipeDefaultTrihedron = "DiscreteTrihedron"; -Geometry.AutoCoherence = 1; -Geometry.Clip = 0; -Geometry.CopyMeshingMethod = 0; -Geometry.Curves = 1; -Geometry.CurveLabels = 0; -Geometry.CurveSelectWidth = 3; -Geometry.CurveType = 0; -Geometry.CurveWidth = 2; -Geometry.DoubleClickedEntityTag = 0; -Geometry.ExactExtrusion = 1; -Geometry.ExtrudeReturnLateralEntities = 1; -Geometry.ExtrudeSplinePoints = 5; -Geometry.HighlightOrphans = 0; -Geometry.LabelType = 0; -Geometry.Light = 1; -Geometry.LightTwoSide = 1; -Geometry.MatchGeomAndMesh = 0; -Geometry.MatchMeshScaleFactor = 1; -Geometry.MatchMeshTolerance = 1e-06; -Geometry.Normals = 0; -Geometry.NumSubEdges = 40; -Geometry.OCCAutoEmbed = 1; -Geometry.OCCAutoFix = 1; -Geometry.OCCBooleanPreserveNumbering = 1; -Geometry.OCCBoundsUseStl = 0; -Geometry.OCCDisableStl = 0; -Geometry.OCCFixDegenerated = 0; -Geometry.OCCFixSmallEdges = 0; -Geometry.OCCFixSmallFaces = 0; -Geometry.OCCExportOnlyVisible = 0; -Geometry.OCCImportLabels = 1; -Geometry.OCCMakeSolids = 0; -Geometry.OCCParallel = 0; -Geometry.OCCSafeUnbind = 0; -Geometry.OCCScaling = 1; -Geometry.OCCSewFaces = 0; -Geometry.OCCThruSectionsDegree = -1; -Geometry.OCCUnionUnify = 1; -Geometry.OCCUseGenericClosestPoint = 0; -Geometry.OffsetX = 0; -Geometry.OffsetY = 0; -Geometry.OffsetZ = 0; -Geometry.OldCircle = 0; -Geometry.OldRuledSurface = 0; -Geometry.OldNewReg = 1; -Geometry.OrientedPhysicals = 1; -Geometry.Points = 1; -Geometry.PointLabels = 0; -Geometry.PointSelectSize = 6; -Geometry.PointSize = 4; -Geometry.PointType = 0; -Geometry.ReparamOnFaceRobust = 0; -Geometry.ScalingFactor = 1; -Geometry.SnapPoints = 1; -Geometry.SnapX = 0.1; -Geometry.SnapY = 0.1; -Geometry.SnapZ = 0.1; -Geometry.Surfaces = 0; -Geometry.SurfaceLabels = 0; -Geometry.SurfaceType = 0; -Geometry.Tangents = 0; -Geometry.Tolerance = 1e-08; -Geometry.ToleranceBoolean = 0; -Geometry.Transform = 0; -Geometry.TransformXX = 1; -Geometry.TransformXY = 0; -Geometry.TransformXZ = 0; -Geometry.TransformYX = 0; -Geometry.TransformYY = 1; -Geometry.TransformYZ = 0; -Geometry.TransformZX = 0; -Geometry.TransformZY = 0; -Geometry.TransformZZ = 1; -Geometry.Volumes = 0; -Geometry.VolumeLabels = 0; -Geometry.Color.Points = {90,90,90}; -Geometry.Color.Curves = {0,0,255}; -Geometry.Color.Surfaces = {128,128,128}; -Geometry.Color.Volumes = {255,255,0}; -Geometry.Color.Selection = {255,0,0}; -Geometry.Color.HighlightZero = {255,0,0}; -Geometry.Color.HighlightOne = {255,150,0}; -Geometry.Color.HighlightTwo = {255,255,0}; -Geometry.Color.Tangents = {255,255,0}; -Geometry.Color.Normals = {255,0,0}; -Geometry.Color.Projection = {0,255,0}; -Mesh.Algorithm = 6; -Mesh.Algorithm3D = 1; -Mesh.AlgorithmSwitchOnFailure = 1; -Mesh.AngleSmoothNormals = 30; -Mesh.AngleToleranceFacetOverlap = 0.1; -Mesh.AnisoMax = 9.999999999999999e+32; -Mesh.AllowSwapAngle = 10; -Mesh.BdfFieldFormat = 1; -Mesh.Binary = 0; -Mesh.BoundaryLayerFanElements = 5; -Mesh.CgnsImportOrder = 1; -Mesh.CgnsImportIgnoreBC = 0; -Mesh.CgnsImportIgnoreSolution = 0; -Mesh.CgnsConstructTopology = 0; -Mesh.CgnsExportCPEX0045 = 0; -Mesh.CgnsExportStructured = 0; -Mesh.Clip = 0; -Mesh.ColorCarousel = 1; -Mesh.CompoundClassify = 1; -Mesh.CompoundMeshSizeFactor = 0.5; -Mesh.CpuTime = 0.006966399996599648; -Mesh.CreateTopologyMsh2 = 0; -Mesh.DrawSkinOnly = 0; -Mesh.Dual = 0; -Mesh.ElementOrder = 1; -Mesh.Explode = 1; -Mesh.FirstElementTag = 1; -Mesh.FirstNodeTag = 1; -Mesh.FlexibleTransfinite = 0; -Mesh.Format = 10; -Mesh.Hexahedra = 1; -Mesh.HighOrderDistCAD = 0; -Mesh.HighOrderFixBoundaryNodes = 0; -Mesh.HighOrderIterMax = 100; -Mesh.HighOrderNumLayers = 6; -Mesh.HighOrderOptimize = 0; -Mesh.HighOrderPassMax = 25; -Mesh.HighOrderPeriodic = 0; -Mesh.HighOrderPoissonRatio = 0.33; -Mesh.HighOrderSavePeriodic = 0; -Mesh.HighOrderPrimSurfMesh = 0; -Mesh.HighOrderThresholdMin = 0.1; -Mesh.HighOrderThresholdMax = 2; -Mesh.HighOrderFastCurvingNewAlgo = 0; -Mesh.HighOrderCurveOuterBL = 0; -Mesh.HighOrderMaxRho = 0.3; -Mesh.HighOrderMaxAngle = 0.1745329277777778; -Mesh.HighOrderMaxInnerAngle = 0.5235987833333333; -Mesh.IgnoreParametrization = 0; -Mesh.IgnorePeriodicity = 1; -Mesh.LabelSampling = 1; -Mesh.LabelType = 0; -Mesh.LcIntegrationPrecision = 1e-09; -Mesh.Light = 1; -Mesh.LightLines = 2; -Mesh.LightTwoSide = 1; -Mesh.Lines = 0; -Mesh.LineLabels = 0; -Mesh.LineWidth = 1; -Mesh.MaxIterDelaunay3D = 0; -Mesh.MaxNumThreads1D = 0; -Mesh.MaxNumThreads2D = 0; -Mesh.MaxNumThreads3D = 0; -Mesh.MaxRetries = 10; -Mesh.MeshOnlyVisible = 0; -Mesh.MeshOnlyEmpty = 0; -Mesh.MeshSizeExtendFromBoundary = 1; -Mesh.MeshSizeFactor = 1; -Mesh.MeshSizeMin = 0; -Mesh.MeshSizeMax = 1e+22; -Mesh.MeshSizeFromCurvature = 0; -Mesh.MeshSizeFromCurvatureIsotropic = 0; -Mesh.MeshSizeFromPoints = 1; -Mesh.MeshSizeFromParametricPoints = 0; -Mesh.MetisAlgorithm = 1; -Mesh.MetisEdgeMatching = 2; -Mesh.MetisMaxLoadImbalance = -1; -Mesh.MetisObjective = 1; -Mesh.MetisMinConn = -1; -Mesh.MetisRefinementAlgorithm = 2; -Mesh.MinimumLineNodes = 2; -Mesh.MinimumCircleNodes = 7; -Mesh.MinimumCurveNodes = 3; -Mesh.MinimumElementsPerTwoPi = 0; -Mesh.MshFileVersion = 4.1; -Mesh.MedFileMinorVersion = -1; -Mesh.MedImportGroupsOfNodes = 0; -Mesh.MedSingleModel = 0; -Mesh.NbHexahedra = 0; -Mesh.NbNodes = 143; -Mesh.NbPartitions = 0; -Mesh.NbPrisms = 0; -Mesh.NbPyramids = 0; -Mesh.NbTrihedra = 0; -Mesh.NbQuadrangles = 0; -Mesh.NbTetrahedra = 420; -Mesh.NbTriangles = 220; -Mesh.NewtonConvergenceTestXYZ = 0; -Mesh.Nodes = 0; -Mesh.NodeLabels = 0; -Mesh.NodeSize = 4; -Mesh.NodeType = 0; -Mesh.Normals = 0; -Mesh.NumSubEdges = 2; -Mesh.OldInitialDelaunay2D = 0; -Mesh.Optimize = 1; -Mesh.OptimizeThreshold = 0.3; -Mesh.OptimizeNetgen = 0; -Mesh.PartitionHexWeight = -1; -Mesh.PartitionLineWeight = -1; -Mesh.PartitionPrismWeight = -1; -Mesh.PartitionPyramidWeight = -1; -Mesh.PartitionQuadWeight = -1; -Mesh.PartitionTrihedronWeight = 0; -Mesh.PartitionTetWeight = -1; -Mesh.PartitionTriWeight = -1; -Mesh.PartitionCreateTopology = 1; -Mesh.PartitionCreatePhysicals = 1; -Mesh.PartitionCreateGhostCells = 0; -Mesh.PartitionSplitMeshFiles = 0; -Mesh.PartitionTopologyFile = 0; -Mesh.PartitionOldStyleMsh2 = 1; -Mesh.PartitionConvertMsh2 = 1; -Mesh.PreserveNumberingMsh2 = 0; -Mesh.Prisms = 1; -Mesh.Pyramids = 1; -Mesh.QuadqsSizemapMethod = 3; -Mesh.QuadqsTopologyOptimizationMethods = 0; -Mesh.QuadqsRemeshingBoldness = 0.66; -Mesh.QuadqsScalingOnTriangulation = 0.75; -Mesh.Quadrangles = 1; -Mesh.QualityInf = 0; -Mesh.QualitySup = 0; -Mesh.QualityType = 2; -Mesh.RadiusInf = 0; -Mesh.RadiusSup = 0; -Mesh.RandomFactor = 1e-09; -Mesh.RandomFactor3D = 1e-12; -Mesh.RandomSeed = 1; -Mesh.ReadGroupsOfElements = 1; -Mesh.RecombinationAlgorithm = 1; -Mesh.RecombineAll = 0; -Mesh.RecombineOptimizeTopology = 5; -Mesh.RecombineNodeRepositioning = 1; -Mesh.RecombineMinimumQuality = 0.01; -Mesh.Recombine3DAll = 0; -Mesh.Recombine3DLevel = 0; -Mesh.Recombine3DConformity = 0; -Mesh.RefineSteps = 10; -Mesh.Renumber = 1; -Mesh.ReparamMaxTriangles = 250000; -Mesh.SaveAll = 0; -Mesh.SaveElementTagType = 1; -Mesh.SaveGroupsOfElements = 1; -Mesh.SaveGroupsOfNodes = 0; -Mesh.SaveParametric = 0; -Mesh.SaveWithoutOrphans = 0; -Mesh.SaveTopology = 0; -Mesh.ScalingFactor = 1; -Mesh.SecondOrderIncomplete = 0; -Mesh.SecondOrderLinear = 0; -Mesh.Smoothing = 1; -Mesh.SmoothCrossField = 0; -Mesh.CrossFieldClosestPoint = 1; -Mesh.SmoothNormals = 0; -Mesh.SmoothRatio = 1.8; -Mesh.StlAngularDeflection = 0.3; -Mesh.StlLinearDeflection = 0.001; -Mesh.StlLinearDeflectionRelative = 1; -Mesh.StlOneSolidPerSurface = 0; -Mesh.StlRemoveDuplicateTriangles = 0; -Mesh.SubdivisionAlgorithm = 0; -Mesh.SurfaceEdges = 1; -Mesh.SurfaceFaces = 0; -Mesh.SurfaceLabels = 0; -Mesh.SwitchElementTags = 0; -Mesh.Tangents = 0; -Mesh.Tetrahedra = 1; -Mesh.ToleranceEdgeLength = 0; -Mesh.ToleranceInitialDelaunay = 1e-12; -Mesh.ToleranceReferenceElement = 1e-06; -Mesh.Triangles = 1; -Mesh.Trihedra = 1; -Mesh.TransfiniteTri = 0; -Mesh.UnvStrictFormat = 1; -Mesh.VolumeEdges = 1; -Mesh.VolumeFaces = 0; -Mesh.VolumeLabels = 0; -Mesh.Voronoi = 0; -Mesh.ZoneDefinition = 0; -Mesh.Color.Nodes = {0,0,255}; -Mesh.Color.NodesSup = {255,0,255}; -Mesh.Color.Lines = {0,0,0}; -Mesh.Color.Triangles = {160,150,255}; -Mesh.Color.Quadrangles = {130,120,225}; -Mesh.Color.Tetrahedra = {160,150,255}; -Mesh.Color.Hexahedra = {130,120,225}; -Mesh.Color.Prisms = {232,210,23}; -Mesh.Color.Pyramids = {217,113,38}; -Mesh.Color.Trihedra = {20,255,0}; -Mesh.Color.Tangents = {255,255,0}; -Mesh.Color.Normals = {255,0,0}; -Mesh.Color.Zero = {255,120,0}; -Mesh.Color.One = {0,255,132}; -Mesh.Color.Two = {255,160,0}; -Mesh.Color.Three = {0,255,192}; -Mesh.Color.Four = {255,200,0}; -Mesh.Color.Five = {0,216,255}; -Mesh.Color.Six = {255,240,0}; -Mesh.Color.Seven = {0,176,255}; -Mesh.Color.Eight = {228,255,0}; -Mesh.Color.Nine = {0,116,255}; -Mesh.Color.Ten = {188,255,0}; -Mesh.Color.Eleven = {0,76,255}; -Mesh.Color.Twelve = {148,255,0}; -Mesh.Color.Thirteen = {24,0,255}; -Mesh.Color.Fourteen = {108,255,0}; -Mesh.Color.Fifteen = {84,0,255}; -Mesh.Color.Sixteen = {68,255,0}; -Mesh.Color.Seventeen = {104,0,255}; -Mesh.Color.Eighteen = {0,255,52}; -Mesh.Color.Nineteen = {184,0,255}; -Solver.Executable0 = ""; -Solver.Executable1 = ""; -Solver.Executable2 = ""; -Solver.Executable3 = ""; -Solver.Executable4 = ""; -Solver.Executable5 = ""; -Solver.Executable6 = ""; -Solver.Executable7 = ""; -Solver.Executable8 = ""; -Solver.Executable9 = ""; -Solver.Name0 = "GetDP"; -Solver.Name1 = ""; -Solver.Name2 = ""; -Solver.Name3 = ""; -Solver.Name4 = ""; -Solver.Name5 = ""; -Solver.Name6 = ""; -Solver.Name7 = ""; -Solver.Name8 = ""; -Solver.Name9 = ""; -Solver.Extension0 = ".pro"; -Solver.Extension1 = ""; -Solver.Extension2 = ""; -Solver.Extension3 = ""; -Solver.Extension4 = ""; -Solver.Extension5 = ""; -Solver.Extension6 = ""; -Solver.Extension7 = ""; -Solver.Extension8 = ""; -Solver.Extension9 = ""; -Solver.OctaveInterpreter = "octave"; -Solver.PythonInterpreter = "python"; -Solver.RemoteLogin0 = ""; -Solver.RemoteLogin1 = ""; -Solver.RemoteLogin2 = ""; -Solver.RemoteLogin3 = ""; -Solver.RemoteLogin4 = ""; -Solver.RemoteLogin5 = ""; -Solver.RemoteLogin6 = ""; -Solver.RemoteLogin7 = ""; -Solver.RemoteLogin8 = ""; -Solver.RemoteLogin9 = ""; -Solver.SocketName = ".gmshsock"; -Solver.AlwaysListen = 0; -Solver.AutoArchiveOutputFiles = 0; -Solver.AutoCheck = 1; -Solver.AutoLoadDatabase = 0; -Solver.AutoSaveDatabase = 1; -Solver.AutoMesh = 2; -Solver.AutoMergeFile = 1; -Solver.AutoShowViews = 2; -Solver.AutoShowLastStep = 1; -Solver.Plugins = 0; -Solver.ShowInvisibleParameters = 0; -Solver.Timeout = 5; -PostProcessing.DoubleClickedGraphPointCommand = ""; -PostProcessing.GraphPointCommand = ""; -PostProcessing.AnimationDelay = 0.1; -PostProcessing.AnimationCycle = 0; -PostProcessing.AnimationStep = 1; -PostProcessing.Binary = 0; -PostProcessing.CombineRemoveOriginal = 1; -PostProcessing.CombineCopyOptions = 1; -PostProcessing.DoubleClickedGraphPointX = 0; -PostProcessing.DoubleClickedGraphPointY = 0; -PostProcessing.DoubleClickedView = 0; -PostProcessing.ForceElementData = 0; -PostProcessing.ForceNodeData = 0; -PostProcessing.Format = 10; -PostProcessing.GraphPointX = 0; -PostProcessing.GraphPointY = 0; -PostProcessing.HorizontalScales = 1; -PostProcessing.Link = 0; -PostProcessing.NbViews = 0; -PostProcessing.Plugins = 1; -PostProcessing.SaveInterpolationMatrices = 1; -PostProcessing.SaveMesh = 1; -PostProcessing.Smoothing = 0; -Print.ParameterCommand = "Mesh.Clip=1; View.Clip=1; General.ClipWholeElements=1; General.Clip0D=Print.Parameter; SetChanged;"; -Print.Parameter = 0; -Print.ParameterFirst = -1; -Print.ParameterLast = 1; -Print.ParameterSteps = 10; -Print.Background = 0; -Print.CompositeWindows = 0; -Print.DeleteTemporaryFiles = 1; -Print.EpsBestRoot = 1; -Print.EpsCompress = 0; -Print.EpsLineWidthFactor = 1; -Print.EpsOcclusionCulling = 1; -Print.EpsPointSizeFactor = 1; -Print.EpsPS3Shading = 0; -Print.EpsQuality = 1; -Print.Format = 10; -Print.GeoLabels = 1; -Print.GeoOnlyPhysicals = 0; -Print.GifDither = 0; -Print.GifInterlace = 0; -Print.GifSort = 1; -Print.GifTransparent = 0; -Print.Height = -1; -Print.JpegQuality = 100; -Print.JpegSmoothing = 0; -Print.PgfTwoDim = 1; -Print.PgfExportAxis = 0; -Print.PgfHorizontalBar = 0; -Print.PostElementary = 1; -Print.PostElement = 0; -Print.PostGamma = 0; -Print.PostEta = 0; -Print.PostSICN = 0; -Print.PostSIGE = 0; -Print.PostDisto = 0; -Print.TexAsEquation = 0; -Print.TexForceFontSize = 0; -Print.TexWidthInMm = 150; -Print.Text = 1; -Print.X3dCompatibility = 0; -Print.X3dPrecision = 1e-09; -Print.X3dRemoveInnerBorders = 0; -Print.X3dTransparency = 0; -Print.X3dSurfaces = 1; -Print.X3dEdges = 0; -Print.X3dVertices = 0; -Print.X3dVolumes = 0; -Print.X3dColorize = 0; -Print.Width = -1; diff --git a/.vscode/settings.json b/.vscode/settings.json index ba77eac..b9fb3c8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,5 +2,8 @@ "[python]": { "editor.defaultFormatter": "ms-python.python" }, - "python.formatting.provider": "none" + "python.formatting.provider": "none", + "docify.commentService.docstringFormat": "Doxygen", + "docify.translateService.docstringFormat": "Doxygen", + "docify.commentService.sidePanelReviewMode": true } \ No newline at end of file diff --git a/amworkflow/src/constants/data_model.py b/amworkflow/src/constants/data_model.py index 6e94e44..59cd978 100644 --- a/amworkflow/src/constants/data_model.py +++ b/amworkflow/src/constants/data_model.py @@ -3,10 +3,11 @@ from polyfactory.factories.pydantic_factory import ModelFactory from amworkflow.src.utils.parser import yaml_parser from amworkflow.src.constants.enums import Directory as D +import numpy as np class MapParamModel(object): def __init__(self, label: list, data: list = None): - self.data = data if data != None else [None for i in range(len(label))] + self.data = data if isinstance(data, list) or isinstance(data, np.ndarray) else [None for i in range(len(label))] self.label = label self.mapping() self.dict = dict(zip(self.label, self.data)) @@ -15,8 +16,12 @@ def mapping(self): for ind, lbl in enumerate(self.label): setattr(self, lbl, self.data[ind]) +class DeepMapParamModel(object): + def __init__(self, target_dict: dict): + self.d = target_dict + for key, value in self.d.items(): + if isinstance(value, dict): + setattr(self, key, DeepMapParamModel(value)) + else: + setattr(self, key, value) -# lab = ["a", "b", "c"] -# # val = [2, 3, 1] -# dat = MapParamModel(lab) -# print(dat.a) \ No newline at end of file diff --git a/amworkflow/src/constants/enums.py b/amworkflow/src/constants/enums.py index 4fb8258..e7a04a5 100644 --- a/amworkflow/src/constants/enums.py +++ b/amworkflow/src/constants/enums.py @@ -24,7 +24,10 @@ class Label(Enum): BATCH_PARAM = "batch_parameter" IS_BATCH = "isbatch" MESH_PARAM = "mesh_parameter" + MESH_SIZE_FACTOR = "mesh_size_factor" STL_PARAM = "stl_parameter" + LNR_DFT = "linear_deflection" + ANG_DFT = "angular_deflection" MDL_PROF = "model_profile" MDL_NAME = "model_name" MDL_PARAM = "model_parameter" diff --git a/amworkflow/src/core/workflow.py b/amworkflow/src/core/workflow.py index 75c2012..d480598 100644 --- a/amworkflow/src/core/workflow.py +++ b/amworkflow/src/core/workflow.py @@ -1,11 +1,10 @@ from OCC.Core.TopoDS import TopoDS_Shape -import yaml -import os +import sys from amworkflow.src.constants.enums import Directory as D from amworkflow.src.constants.enums import Label as L from amworkflow.src.utils.parser import yaml_parser, cmd_parser import gmsh -from amworkflow.src.infrastructure.database.models.model import XdmfFile, H5File, FEResult, SliceFile, GCode, ModelProfile, ModelParameter +from amworkflow.src.infrastructure.database.models.model import XdmfFile, H5File, FEResult, SliceFile, GCode, ModelProfile, ModelParameter, GeometryFile from amworkflow.src.infrastructure.database.cruds.crud import insert_data, query_multi_data, delete_data from amworkflow.src.geometries.mesher import mesher, get_geom_pointer from amworkflow.src.utils.writer import mesh_writer @@ -13,25 +12,28 @@ from amworkflow.src.utils.writer import namer, stl_writer, batch_num_creator import numpy as np from amworkflow.src.utils.download import downloader -from amworkflow.src.constants.data_model import MapParamModel +from amworkflow.src.constants.data_model import MapParamModel, DeepMapParamModel from amworkflow.src.utils.sanity_check import path_valid_check, dimension_check from amworkflow.src.constants.exceptions import NoDataInDatabaseException, InsufficientDataException from amworkflow.src.utils.reader import get_filename -from amworkflow.src.utils.reader import step_reader +from amworkflow.src.utils.reader import step_reader, stl_reader +from amworkflow.src.interface.cli.cli_workflow import cli class BaseWorkflow(object): - def __init__(self, args): - self.raw_args = args - self.args = cmd_parser(args) + def __init__(self): + self.raw_args = cli() + print(self.raw_args) + self.mpm = MapParamModel + self.dmpm = DeepMapParamModel + self.parsed_args = cmd_parser(self.raw_args) + self.parsed_args_c = self.dmpm(self.parsed_args) self.yaml_dir = self.raw_args.yaml_dir - self.step_dir = self.raw_args.step_dir + self.import_dir = self.raw_args.import_dir self.yaml_parser = yaml_parser self.namer = namer self.model_name = self.raw_args.name - self.isbatch = self.args[L.BATCH_PARAM.value][L.IS_BATCH.value] self.model_hashname = self.namer(name_type="hex") self.geom_pointer: int - self.mpm = MapParamModel self.shape = [] self.mesh_result = [] self.name_list = [] @@ -45,32 +47,40 @@ def __init__(self, args): self.num_vector = [] self.db_data_collection = {} self.db_del_collection = [] - self.batch_num = None + self.batch_num = self.parsed_args + self.isbatch = False + self.linear_deflect = 0.001 + self.angular_deflect = 0.1 + self.mesh_t = None + self.mesh_n = None + self.mesh_s = None + self.task_handler() self.data_init() self.permutation = self.permutator() + - def data_init(self): + def task_handler(self): if self.model_name != None: result = query_multi_data(ModelProfile, by_name=self.model_name, column_name=L.MDL_NAME.value, target_column_name=L.MDL_NAME.value) if self.model_name in result: self.indicator = (0,0) - if self.args.edit: + if self.raw_args.edit: self.indicator = (0,1) else: - if self.args.remove: + if self.raw_args.remove: self.indicator = (0,2) else: self.indicator = (0,3) else: self.indicator = (0,4) else: - if self.step_dir != None: - path_valid_check(self.step_dir, format=["stp", "step"]) - self.stp_filename = get_filename(self.step_dir) - result = query_multi_data(ModelProfile, by_name=self.stp_filename, column_name=L.MDL_NAME.value, target_column_name=L.MDL_NAME.value) - if self.stp_filename in result: + if self.import_dir != None: + self.impt_format = path_valid_check(self.import_dir, format=["stp", "step","stl","STL"]) + self.impt_filename = get_filename(self.import_dir) + result = query_multi_data(ModelProfile, by_name=self.impt_filename, column_name=L.MDL_NAME.value, target_column_name=L.MDL_NAME.value) + if self.impt_filename in result: self.indicator = (1,0) - if self.args.remove: + if self.parsed_args.remove: self.indicator = (1,2) else: self.indicator = (1,1) @@ -84,14 +94,35 @@ def data_init(self): else: raise InsufficientDataException() else: raise InsufficientDataException() - + + def data_init(self): match self.indicator[0]: case 1: #read the step file stored in the database and convert it to an OCC representation. - self.import_model = step_reader(path=D.DATABASE_OUTPUT_FILE_PATH.value+self.stp_filename) + + self.linear_deflect = self.raw_args.stl_linear_deflect + self.angular_deflect= self.raw_args.stl_angular_deflect + self.mesh_t = self.raw_args.mesh_by_thickness + self.mesh_n = self.raw_args.mesh_by_layer + self.mesh_s = self.raw_args.mesh_size_factor + self.isbatch = self.parsed_args[L.BATCH_PARAM.value][L.IS_BATCH.value] + match self.impt_format.lower(): + case "stl": + self.import_model = stl_reader(path=self.import_dir) + case "stp": + self.import_model = step_reader(path=self.import_dir) + case "step": + self.import_model = step_reader(path=D.DATABASE_OUTPUT_FILE_PATH.value+self.impt_filename) match self.indicator[1]: case 1: # Import stp file, convert the file to an OCC representation and create a new profile for imported stp file. - self.import_model = step_reader(self.step_dir) - self.db_data_collection[L.MDL_PROF.value] = {L.MDL_NAME.value: self.stp_filename} + match self.impt_format.lower(): + case "stl": + self.import_model = stl_reader(path=D.DATABASE_OUTPUT_FILE_PATH.value+self.impt_filename) + case "stp": + self.import_model = step_reader(path=D.DATABASE_OUTPUT_FILE_PATH.value+self.impt_filename) + case "tep": + self.import_model = step_reader(path=D.DATABASE_OUTPUT_FILE_PATH.value+self.impt_filename) + self.db_data_collection[L.MDL_PROF.value] = {L.MDL_NAME.value: self.impt_filename} + # self.batch_data_convert(self.parsed_args[L.GEOM_PARAM.value]) case 2: # remove selected profile and stp file, then quit #TODO remove the step file and and info in db. pass @@ -100,8 +131,24 @@ def data_init(self): self.batch_data_convert(data=self.geom_data) self.param_type = self.geom_data.keys() self.mesh_param = self.data[L.MESH_PARAM.value] + self.linear_deflect = self.data[L.STL_PARAM.value][L.LNR_DFT.value] + self.angular_deflect = self.data[L.STL_PARAM.value][L.ANG_DFT.value] + self.mesh_t = self.data[L.MESH_PARAM.value][L.LYR_TKN.value] + self.mesh_n = self.data[L.MESH_PARAM.value][L.LYR_NUM.value] + self.mesh_s = self.data[L.MESH_PARAM.value][L.MESH_SIZE_FACTOR.value] + if sum(self.num_vector) > 1: + self.isbatch = True + self.batch_data_convert(self.parsed_args[L.GEOM_PARAM.value]) + else: + self.isbatch = False case 0: # model_name provided + self.linear_deflect = self.raw_args.stl_linear_deflect + self.angular_deflect= self.raw_args.stl_angular_deflect + self.mesh_t = self.raw_args.mesh_by_thickness + self.mesh_n = self.raw_args.mesh_by_layer + self.mesh_s = self.raw_args.mesh_size_factor + self.isbatch = self.parsed_args[L.BATCH_PARAM.value][L.IS_BATCH.value] self.query_list = query_multi_data(table = ModelParameter, by_name= self.model_name, column_name=L.MDL_NAME.value) @@ -109,9 +156,9 @@ def data_init(self): if self.indicator[1] == 1: # edit mode. edit profile, replace the old and continue with new parameters. self.db_del_collection.append(ModelParameter,self.param_type) self.db_delete() - self.param_type = self.args[L.GEOM_PARAM.value].keys() + self.param_type = self.parsed_args[L.GEOM_PARAM.value].keys() value = [{L.PARAM_NAME.value: new_param[i], - L.MDL_NAME.value: self.stp_filename} for i in range(len(self.param_type))] + L.MDL_NAME.value: self.impt_filename} for i in range(len(self.param_type))] self.db_insert(ModelParameter, value) match self.indicator[1]: case 2: @@ -119,18 +166,18 @@ def data_init(self): self.db_del_collection.append(ModelProfile, [self.model_name]) self.db_delete() case 3: # do nothing, fill data into the loaded model. - self.param_type = self.args[L.GEOM_PARAM.value].keys() + self.param_type = self.parsed_args[L.GEOM_PARAM.value].keys() case 4: # Create a new model profile with given parameters. self.db_data_collection[L.MDL_PROF.value] = [{L.MDL_NAME.value: self.raw_args.name}] - new_param = list(self.args[L.GEOM_PARAM.value].keys()) + new_param = list(self.parsed_args[L.GEOM_PARAM.value].keys()) print(new_param) value = [{L.PARAM_NAME.value: new_param[i], L.MDL_NAME.value: self.model_name} for i in range(len(new_param))] self.db_data_collection[L.MDL_PARAM.value] = value - self.param_type = self.args[L.GEOM_PARAM.value].keys() + self.param_type = list(self.parsed_args[L.GEOM_PARAM.value].keys()) self.db_insert(ModelProfile, self.db_data_collection[L.MDL_PROF.value]) self.db_insert(ModelParameter, self.db_data_collection[L.MDL_PARAM.value]) - self.batch_data_convert(data=self.args[L.GEOM_PARAM.value]) + self.batch_data_convert(data=self.parsed_args[L.GEOM_PARAM.value]) def create(self) -> None: ''' @@ -151,25 +198,27 @@ def create(self) -> None: else: self.geom_process(iter_ind, iter_perm, "dimension-batch") if self.db: - self.db_insert(db_model=self.geom_db_model, data=self.db_data_collection["geometry"]) + self.db_insert(db_model=GeometryFile, data=self.db_data_collection["geometry"]) for ind, item in enumerate(self.shape): + print(self.angular_deflect) stl_writer(item=item, item_name=self.hashname_list[ind] + ".stl", - linear_deflection= self.data.stl_parameter.linear_deflection, - angular_deflection= self.data.stl_parameter.angular_deflection) + linear_deflection= self.linear_deflect, + angular_deflection= self.angular_deflect) def geometry_spawn(self, param) -> TopoDS_Shape: '''Define a parameterized model using PyOCC APIs here with parameters defined in the yaml file. Return one single TopoDs_shape.''' return TopoDS_Shape def geom_process(self, ind: int, param: list, name_type: str): - param = self.mpm(param, self.param_type) + param = self.mpm(self.param_type, param) tp_geom = self.geometry_spawn(param) self.shape.append(tp_geom) hash_name, filename = self.item_namer(name_type=name_type, ind=ind) append_data = {"batch_num" : self.batch_num, "geom_hashname" : hash_name, - "filename" : filename} + "filename" : filename, + "model_name": self.model_name} self.db_data_collection["geometry"].append(append_data) def mesh(self): @@ -177,18 +226,21 @@ def mesh(self): mesh the geom created by create() ''' if self.indicator == (2,0): - data=self.mesh_param.values() - label=self.mesh_param.keys() + data=[self.mesh_n, self.mesh_t, self.mesh_s] + label=list(self.mesh_param.keys()) else: - data=self.args[L.MESH_PARAM.value].values() - label=self.args[L.MESH_PARAM.value].keys() + data=[self.mesh_n, self.mesh_t, self.mesh_s] + label=[L.LYR_NUM.value,L.LYR_TKN.value,L.MESH_SIZE_FACTOR.value] mesh_param = self.mpm(data=data, label=label) + print(mesh_param.dict) + print(mesh_param.layer_thickness) + is_thickness = True if mesh_param.layer_thickness != None else False + size_factor = mesh_param.mesh_size_factor gmsh.initialize() self.db_data_collection["mesh"] = {"xdmf": [], "h5": []} for index, item in enumerate(self.shape): - is_thickness = True if L.LYR_TKN != None in self.raw_args[L.LYR_TKN.value] else False - size_factor = mesh_param.mesh_size_factor + if is_thickness: layer_param = mesh_param.layer_thickness else: @@ -286,7 +338,7 @@ def item_namer(self, name_type, ind): hash_name = self.namer(name_type="hex") self.hashname_list.append(hash_name) filename = self.namer(name_type=name_type, - parm_title = self.title, + parm_title = self.param_type, dim_vector=self.permutation[ind], batch_num=self.batch_num) + ".stl" self.name_list.append(filename) diff --git a/amworkflow/src/geometries/builder.py b/amworkflow/src/geometries/builder.py index 0a00881..9a8391c 100644 --- a/amworkflow/src/geometries/builder.py +++ b/amworkflow/src/geometries/builder.py @@ -1,9 +1,21 @@ from OCC.Core.BRep import BRep_Builder from OCC.Core.TopoDS import TopoDS_Compound - +from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_Sewing, BRepBuilderAPI_MakeSolid +from OCC.Core.TopoDS import TopoDS_Shape, TopoDS_Wire, TopoDS_Shell def geometry_builder(*args): builder = BRep_Builder() obj = TopoDS_Compound() builder.MakeCompound(obj) for item in args[0]: builder.Add(obj, item) - return obj \ No newline at end of file + return obj + +def sewer(*component) -> TopoDS_Shape: + sewing = BRepBuilderAPI_Sewing() + for i in range(len(component[0])): + sewing.Add(component[0][i]) + sewing.Perform() + sewed_shape = sewing.SewedShape() + return sewed_shape + +def solid_maker(item: TopoDS_Shape) -> TopoDS_Shape: + return BRepBuilderAPI_MakeSolid(item).Shape() \ No newline at end of file diff --git a/amworkflow/src/geometries/composite_geometry.py b/amworkflow/src/geometries/composite_geometry.py new file mode 100644 index 0000000..3db3ce7 --- /dev/null +++ b/amworkflow/src/geometries/composite_geometry.py @@ -0,0 +1,295 @@ +from amworkflow.src.geometries.simple_geometry import create_edge, create_wire, create_face, create_prism, random_polygon_constructor, angle_of_two_arrays, laterality_indicator, angular_bisector +from amworkflow.src.geometries.operator import reverse, geom_copy, translate, rotate_face, fuser, hollow_carver, cutter3D +from OCC.Core.gp import gp_Pnt +import numpy as np +from amworkflow.src.utils.writer import stl_writer, step_writer +from amworkflow.src.geometries.builder import geometry_builder +from amworkflow.src.geometries.mesher import get_geom_pointer +import gmsh +from amworkflow.src.utils.visualizer import mesh_visualizer +from OCC.Core.TopoDS import TopoDS_Face, TopoDS_Shell +from amworkflow.src.geometries.property import get_face_center_of_mass +from amworkflow.src.geometries.builder import sewer + + + +def polygon_maker(side_num: int, + side_len: float, + rotate: float = None, + bound: bool = False): + """ + @brief Creates a regular polygon. The polygon is oriented counterclockwise around the origin. If bound is True the polygon will be only the boundary (TopoDS_Wire) the polygon. + @param side_num Number of sides of the polygon. + @param side_len Length of the side of the polygon. + @param rotate Rotation angle ( in radians ). Defaults to None which means no rotation. + @param bound output only the boundary. Defaults to False. See documentation for create_wire for more information. + @return face or boundary of the polygon. + """ + vertices = [] + sides = [] + r = side_len / (2 * np.sin(np.pi/side_num)) + ang = np.linspace(0,2*np.pi, side_num + 1) + if rotate != None: + ang += rotate + for i in range(side_num + 1): + if i != side_num: + x = np.sin(ang[i]) * r + y = np.cos(ang[i]) * r + t_pnt = gp_Pnt(x, y, 0) + vertices.append(t_pnt) + if i != 0: + if i == side_num : + t_edge = create_edge(pnt1=vertices[0], pnt2=vertices[-1]) + sides.append(t_edge) + wire = create_wire(wire, t_edge) + elif i == 1: + t_edge = create_edge(pnt1=vertices[i-1], pnt2=t_pnt) + sides.append(t_edge) + wire = create_wire(t_edge) + else: + t_edge = create_edge(pnt1=vertices[i-1], pnt2=t_pnt) + sides.append(t_edge) + wire = create_wire(wire, t_edge) + + face = create_face(wire) + if bound: + return wire + else: + return reverse(face) + +def hexagon_multiplier(side_num: int, side_len: float, iter_num: int, wall: float, center: gp_Pnt = None) -> TopoDS_Face: + """ + @brief Creates a hexagon with multiplier. This is an iterative approach to the topological sorting algorithm. + @param side_num Number of sides in the hexagon. + @param side_len Length of the side ( s ) to be used for the multiplication. + @param iter_num Number of iterations to perform. Default is 1. + @param wall Wall thickness. + @param center Center of the multiplication. Default is original point. + @return TopoDS_Face. Note that it is the caller's responsibility to check if there is enough space + """ + + def multiplier_unit(face_odd, face_even, unit_len): + """ + @brief Fuse a unit of mass to an odd or even side number. + @param face_odd face with odd side number ( numpy array ) + @param face_even face with even side number ( numpy array ) + @param unit_len length of side ( numpy array ) + @return one unit of hexagon multiplication. + """ + if side_num % 2 == 0: + face = face_even + ang = np.linspace(0,2*np.pi, side_num + 1) + else: + face = face_odd + ang = np.linspace(np.pi / side_num,2*np.pi + np.pi / side_num, side_num + 1) + cnt = get_face_center_of_mass(face) + len = unit_len * 0.5 / np.tan(np.pi / side_num) * 2 + face_collect = [face] + fuse = face + for ag in ang: + rot_face = rotate_face(face, ag) + x = len * np.sin(ag) + y = len * np.cos(ag) + translate(rot_face, [cnt[0] + x, cnt[1] + y, 0]) + face_collect.append(rot_face) + fuse = fuser(fuse, rot_face) + return fuse + # This function will generate a hollow carver for each iteration. + for i in range(iter_num): + # This function computes the fuse of the carver. + if i == 0: + face_even = hollow_carver(polygon_maker(side_num, side_len, rotate=-np.pi / side_num), wall) + face_odd = hollow_carver(polygon_maker(side_num, side_len),wall) + fuse = multiplier_unit(face_odd, face_even, unit_len=side_len) + else: + face_even = fuse + # face_odd = rotate_face(fuse,angle = -np.pi / side_num) + face_odd = fuse + fuse = multiplier_unit(face_odd, face_even, unit_len=3 * i * side_len) + # translate center to use list of coordinates + if isinstance(center, gp_Pnt): + translate(fuse, list(center.Coord())) + return fuse + +def isoceles_triangle_maker(bbox_len:float, bbox_wid: float, thickness: float = None): + """ + @brief (Having problem with wall thickness now.) Create isoceles triangulation. This is a function to create isoceles triangulation of a bounding box and its widest corner + @param bbox_len length of bounding box of the triangle + @param bbox_wid width of bounding box of the triangle + @param thickness thickness of the wall of the triangle + @return a hollowed triangle face. + """ + h = bbox_wid + l = bbox_len + t = thickness if thickness != None else 0 + ary = np.array([h,l]) + r1 = np.linalg.norm(ary*0.5, 2) + ang1 = np.arccos((h*0.5) / r1) + ang2 = np.pi - ang1 + r = [h*0.5, r1, r1] + thet = np.arctan(l * 0.5 / h) + thet1 = np.arctan(h / l) + t1 = t / np.sin(thet) + t2 = t / np.sin(thet1) + h1 = h - t - t1 + l1 = h1 * l / h + ary1 = np.array([h1, l1]) + r2 = np.linalg.norm(0.5 * ary1, 2) + ri = [0.5 * h1, r2, r2] + ang = [0, ang2, -ang2] + def worker(r): + """ + @brief Creates a triangle in a polarized coordinate system. + @param r The radius of each vertex. + @return a triangle + """ + pt_lst = [] + # Add a point on the plane. + for i in range(3): + x = r[i] * np.sin(ang[i]) + y = r[i] * np.cos(ang[i]) + pnt = gp_Pnt(x,y,0) + pt_lst.append(pnt) + # Create a wire for each point in the list of points. + for i in range(3): + # Create a wire for the i th point of the point. + if i == 0: + edge = create_edge(pt_lst[i], pt_lst[i+1]) + wire = create_wire(edge) + elif i != 2: + edge = create_edge(pt_lst[i], pt_lst[i+1]) + wire = create_wire(wire, edge) + else: + edge = create_edge(pt_lst[i], pt_lst[0]) + wire = create_wire(wire, edge) + face = create_face(wire) + return reverse(face) + outer_face = worker(r) + # The face to be cuttered. + if t != 0: + inner_face = worker(ri) + new_face = cutter3D(outer_face, inner_face) + return new_face + else: + return outer_face + +def create_sym_hexagon1_infill(total_len: float, total_wid:float, height:float, th: float) : + """ + @brief Create an infill pattern using symmetrical hexagon with defined len, height and numbers. + @param total_len total length of the bounding box. + @param total_wid total wid of the bounding box. + @param height height of the prism. This is the same as height of the hexagon. + @param th thickness of the wall of the hexagon. + @return + """ + p0 = [0,th * 0.5] + p1 = [] + +def create_wall_by_points(pts:list, th: float, isclose:bool, height: float = None, debug: bool = False, output: str = "prism") -> np.ndarray or TopoDS_Face or TopoDS_Shell: + """ + @brief Create a prism wall by points. It takes a list of points as a skeleton of a central path and then build a strip or a loop. + @param pts list of 2D points that define the wall. The algorithm can compute points in 3D theoretically but the result may make no sense. + @param th thickness of the wall. + @param isclose True if the wall is closed (loop) + @param height height of the wall if a prism is needed. + @param debug if True output two groups of points for plotting. + @param output selecting result intended to output. can be varied among "face" and "prism". + @return two arrays or a face or a prism. + """ + th *= 0.5 + opt_pts = [] + vecs = [] + dir_vecs = [] + ths = [] + opt_pts_0 = [] + opt_pts_1 = [] + for i,p in enumerate(pts): + if i != len(pts) - 1: + a1 = pts[i+1] - pts[i] + if i == 0: + if isclose: + dr = angular_bisector(pts[-1] - p, a1) + # ang = angle_of_two_arrays(dir_vecs[i-1],dr) + ang2 = angle_of_two_arrays(laterality_indicator(p - pts[-1], True), dr) + ang_th = ang2 + if ang2 > np.pi / 2: + dr *= -1 + ang_th = np.pi - ang2 + nth = np.abs(th / np.cos(ang_th)) + else: + dr = laterality_indicator(a1, True) + nth = th + else: + dr = angular_bisector(-vecs[i-1], a1) + ang2 = angle_of_two_arrays(laterality_indicator(vecs[i-1], True), dr) + ang_th = ang2 + if ang2 > np.pi / 2: + dr *= -1 + ang_th = np.pi - ang2 + nth = np.abs(th / np.cos(ang_th)) + else: + if isclose: + a1 = pts[0] - pts[i] + dr = angular_bisector(-vecs[i-1], a1) + ang2 = angle_of_two_arrays(laterality_indicator(vecs[i-1], True), dr) + ang_th = ang2 + if ang2 > np.pi / 2: + dr *= -1 + ang_th = np.pi - ang2 + nth = np.abs(th / np.cos(ang_th)) + else: + dr = laterality_indicator(a1, True) + nth = th + vecs.append(a1) + ths.append(nth) + dir_vecs.append(dr) + opt_pts_0.append(dr * nth + p) + opt_pts.append(dr * nth + p) + if isclose: + for i,p in enumerate(pts): + dr = -dir_vecs[i] + nth = ths[i] + opt_pts_1.append(dr * nth + p) + else: + for i,p in enumerate(pts[::-1]): + dr = -dir_vecs[::-1][i] + nth = ths[::-1][i] + if debug: + opt_pts_1.append(dr * nth + p) + else: + opt_pts.append(dr * nth + p) + if debug: + return np.array(opt_pts_0), np.array(opt_pts_1) + else: + gp_pts_0 = [gp_Pnt(i[0],i[1],i[2]) for i in opt_pts_0] + gp_pts_1 = [gp_Pnt(i[0],i[1],i[2]) for i in opt_pts_1] + gp_pts = [gp_Pnt(i[0],i[1],i[2]) for i in opt_pts] + if not isclose: + poly = random_polygon_constructor(gp_pts) + else: + poly_o = random_polygon_constructor(gp_pts_0) + poly_i = random_polygon_constructor(gp_pts_1) + poly = cutter3D(poly_o, poly_i) + match output: + case "face": + if not isclose: + return poly + else: + return poly + case "prism": + pr = create_prism(poly, [0,0,height],True) + return pr + + + + + + + + + + + + + + diff --git a/amworkflow/src/geometries/infiller.py b/amworkflow/src/geometries/infiller.py new file mode 100644 index 0000000..23d3179 --- /dev/null +++ b/amworkflow/src/geometries/infiller.py @@ -0,0 +1,47 @@ +import numpy as np +from OCC.Core.TopoDS import TopoDS_Shape +from amworkflow.src.constants.exceptions import DimensionViolationException +from amworkflow.src.geometries.composite_geometry import polygon_maker, isoceles_triangle_maker +class PolygonInfiller(object): + def __init__(self) -> None: + self.pbox_ln: float + self.pbox_wd: float + self.side_len: float + self.bbox_ratio: float + + def config(self, bbox_len: float, bbox_wid: float, bbox_hgt: float, side_num: float, infill_rate: float = None): + self.bbox_len = bbox_len + self.bbox_wid = bbox_wid + self.bbox_hgt = bbox_hgt + self.side_num = side_num + if (infill_rate < 1) and (infill_rate > 0): + self.infill_rate = infill_rate + else: + raise DimensionViolationException("Invalid infill rate.") + self.bbox_ratio = self.bbox_wid / self.bbox_hgt + + def rglr_plygn_side_len_handler(self): + match self.side_num: + case 3: + ln = self.pbox_ln + wd = 0.5*(3**0.5)*self.ln + case 4: + ln = self.pbox_ln + wd = ln + case 5: + ln = self.pbox_ln / (2 * np.sin(np.deg2rad(18)) + 1) + wd = ln * (np.sin(np.deg2rad(18)) + np.cos(np.deg2rad(18))) + case 6: + ln = self.pbox_ln / 2 + wd = ln + if abs(ln / wd -self.pbox_ln / self.pbox_wd) > 1e-3: + return self.pbox_ln, self.pbox_wd, False + else: + return ln, wd, True + + def raid_creator(self): + + + + def polygon_infiller(side_num: int, side_len:float, bbox_len:float, bbox_wid:float,bbox_hgt:float) -> TopoDS_Shape: + \ No newline at end of file diff --git a/amworkflow/src/geometries/mesher.py b/amworkflow/src/geometries/mesher.py index 56ed49f..261d0d1 100644 --- a/amworkflow/src/geometries/mesher.py +++ b/amworkflow/src/geometries/mesher.py @@ -6,6 +6,12 @@ from amworkflow.src.constants.exceptions import GmshUseBeforeInitializedException import logging +def gmsh_switch(s: bool) -> None: + if s: + gmsh.initialize() + else: + gmsh.finalize() + def get_geom_pointer(model: gmsh.model, shape: TopoDS_Shape) -> list: try: gmsh.is_initialized() diff --git a/amworkflow/src/geometries/operator.py b/amworkflow/src/geometries/operator.py index 1c60547..6bb68f2 100644 --- a/amworkflow/src/geometries/operator.py +++ b/amworkflow/src/geometries/operator.py @@ -3,8 +3,10 @@ from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_Copy from OCC.Core.TopoDS import TopoDS_Shape import numpy as np +import OCC.Core.BRepBuilderAPI as BRepBuilderAPI +import OCC.Core.gp as gp from OCC.Core.gp import gp_Pln -from OCC.Core.gp import gp_Pnt, gp_Vec +from OCC.Core.gp import gp_Pnt, gp_Vec, gp_Ax1, gp_Dir from OCC.Core.BOPAlgo import BOPAlgo_Builder from OCCUtils.Topology import Topo from OCCUtils.Construct import make_face @@ -12,9 +14,21 @@ from amworkflow.src.geometries.builder import geometry_builder from OCC.Core.BRepBndLib import brepbndlib_Add from OCC.Core.Bnd import Bnd_Box +from OCC.Core.BRepClass3d import BRepClass3d_Intersector3d +from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Common +from OCC.Core.TopoDS import TopoDS_Compound, TopoDS_Iterator +from amworkflow.src.geometries.property import get_face_center_of_mass +from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut, BRepAlgoAPI_Fuse +from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_Transform, BRepBuilderAPI_Sewing +from amworkflow.src.geometries.property import get_occ_bounding_box -def translate(item: any, +def translate(item: TopoDS_Shape, vector: list): + """ + @brief Translates the shape by the distance and direction of a given vector. + @param item The item to be translated. It must be a TopoDS_Shape + @param vector The vector to translate the object by. The vector has to be a list with three elements + """ ts_handler = gp_Trsf() ts_handler.SetTranslation(gp_Vec(vector[0], vector[1], @@ -22,21 +36,25 @@ def translate(item: any, loc = TopLoc_Location(ts_handler) item.Move(loc) -def reverse(item:any): +def reverse(item:TopoDS_Shape): + """ + @brief Reverse the shape. + @param item The item to reverse. + @return The reversed item + """ return item.Reversed() -def geom_copy(item: any): +def geom_copy(item: TopoDS_Shape): + """ + @brief Copy a geometry to a new shape. This is a wrapper around BRepBuilderAPI_Copy and can be used to create a copy of a geometry without having to re - create the geometry in the same way. + @param item Geometry to be copied. + @return New geometry that is a copy of the input geometry. + """ wire_top_builder = BRepBuilderAPI_Copy(item) wire_top_builder.Perform(item, True) new_item = wire_top_builder.Shape() return new_item -def get_occ_bounding_box(shape: TopoDS_Shape): - bbox = Bnd_Box() - add_bbox = brepbndlib_Add(shape, bbox) - xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get() - return xmin, ymin, zmin, xmax, ymax, zmax - def split(item: TopoDS_Shape, nz: int = None, layer_thickness: float = None, @@ -45,6 +63,18 @@ def split(item: TopoDS_Shape, split_y: bool = False, nx: int = None, ny: int = None): + """ + @brief Split a TopoDS_Shape into sub - shapes. + @param item TopoDS_Shape to be split. + @param nz Number of z - points to split. + @param layer_thickness Layer thickness ( m ). + @param split_z Split on the Z direction. + @param split_x Split on the X direction. + @param split_y Split on the Y direction. + @param nx Number of sub - shapes in the x - direction. + @param ny Number of sub - shapes in the y - direction. + @return a compound of sub-shapes + """ xmin, ymin, zmin, xmax, ymax, zmax = get_occ_bounding_box(item) plan_len = 1.2 * max(abs(xmin - xmax), abs(ymin - ymax)) z = zmax - zmin @@ -76,4 +106,104 @@ def split(item: TopoDS_Shape, top = Topo(bo.Shape()) geo = geometry_builder(top.solids()) return geo - \ No newline at end of file + +def intersector(item: TopoDS_Shape, + position: float, + axis: str) -> TopoDS_Shape: + """ + @brief Returns the topo shape intersecting the item at the given position. + @param position Position of the plane in world coordinates. + @param axis Axis along which of the direction. + @return TopoDS_Shape with intersection or empty TopoDS_Shape if no intersection is found. + """ + intsctr = BRepAlgoAPI_Common + xmin, ymin, zmin, xmax, ymax, zmax = get_occ_bounding_box(item) + bnd_x = abs(xmin - xmax) * 1.2 + bnd_y = abs(ymin - ymax) * 1.2 + bnd_z = abs(zmin - zmax) * 1.2 + match axis: + case "z": + plan_len = max(bnd_x, bnd_y) + p1, v1 = gp_Pnt(0,0,position), gp_Vec(0, 0, 1) + fc1 = make_face(gp_Pln(p1, vec_to_dir(v1)), -plan_len, plan_len, -plan_len, plan_len) + common = intsctr(item, fc1) + common.Build() + + return TopoDS_Iterator(common.Shape()).Value() + +def scaler(item: TopoDS_Shape, cnt_pnt: gp_Pnt, factor: float) -> TopoDS_Shape: + """ + @brief Scales TopoDS_Shape to a given value. This is useful for scaling shapes that are in a shape with respect to another shape. + @param item TopoDS_Shape to be scaled. + @param cnt_pnt the point of the scaling center. + @param factor Factor to scale the shape by. Default is 1. + @return a scaled TopoDS_Shape with scaling applied to it. + """ + scaling_transform = gp.gp_Trsf() + scaling_transform.SetScale(cnt_pnt, factor) + scaled_shape = BRepBuilderAPI.BRepBuilderAPI_Transform(item, scaling_transform, True).Shape() + return scaled_shape + + +def hollow_carver(face: TopoDS_Shape, factor: float): + """ + @brief (This can be replaced by cutter3D() now.)Carving on a face with a shape scaling down from itself. + @param face TopoDS_Shape to be cutted. + @param factor Factor to be used to scale the cutter. + @return A shape with the cutter in it's center of mass scaled by factor + """ + cnt = get_face_center_of_mass(face, gp_pnt=True) + cutter = scaler(face, cnt, factor) + cut = BRepAlgoAPI_Cut(face, cutter).Shape() + return cut + +def rotate_face(shape: TopoDS_Shape, angle: float, axis: str = "z"): + """ + @brief Rotate the topography by the given angle around the center of mass of the face. + @param shape TopoDS_Shape to be rotated. + @param angle Angle ( in degrees ) to rotate by. + @param axis determine the rotation axis. + @return the rotated shape. + """ + transform = gp_Trsf() + cnt = get_face_center_of_mass(shape, gp_pnt=True) + match axis: + case "z": + ax = gp_Ax1(cnt, gp_Dir(0,0,1)) + case "y": + ax = gp_Ax1(cnt, gp_Dir(0,1,0)) + case "x": + ax = gp_Ax1(cnt, gp_Dir(1,0,0)) + transform.SetRotation(ax, angle) + transformed = BRepBuilderAPI_Transform(shape, transform).Shape() + return transformed + +def fuser(shape1: TopoDS_Shape, shape2: TopoDS_Shape) -> TopoDS_Shape: + """ + @brief Fuse two shapes into one. + @param shape1 first shape to fuse. + @param shape2 second shape to fuse. + @return topoDS_Shape + """ + fuse = BRepAlgoAPI_Fuse(shape1, shape2).Shape() + return fuse + +def cutter3D(shape1: TopoDS_Shape, shape2: TopoDS_Shape) -> TopoDS_Shape: + """ + @brief Cut a TopoDS_Shape from shape1 by shape2. It is possible to use this function to cut an object in 3D + @param shape1 shape that is to be cut + @param shape2 shape that is to be cut. It is possible to use this function to cut an object in 3D + @return a shape that is the result of cutting shape1 by shape2 ( or None if shape1 and shape2 are equal + """ + comm = BRepAlgoAPI_Cut(shape1, shape2) + return comm.Shape() + +def common(shape1: TopoDS_Shape, shape2: TopoDS_Shape) -> TopoDS_Shape: + """ + @brief Common between two TopoDS_Shapes. The result is a shape that has all components of shape1 and shape2 + @param shape1 the first shape to be compared + @param shape2 the second shape to be compared ( must be same shape! ) + @return the common shape or None if there is no common shape between shape1 and shape2 in the sense that both shapes are + """ + comm = BRepAlgoAPI_Common(shape1, shape2) + return comm.Shape() \ No newline at end of file diff --git a/amworkflow/src/geometries/property.py b/amworkflow/src/geometries/property.py new file mode 100644 index 0000000..5a13d0d --- /dev/null +++ b/amworkflow/src/geometries/property.py @@ -0,0 +1,111 @@ +from OCC.Core.GProp import GProp_GProps +from OCC.Core.BRepGProp import brepgprop_VolumeProperties, brepgprop_SurfaceProperties +from OCC.Extend.TopologyUtils import TopologyExplorer +from OCC.Core.TopoDS import TopoDS_Face, TopoDS_Solid, TopoDS_Shape +from OCC.Core.Bnd import Bnd_Box +from OCC.Core.BRepBndLib import brepbndlib_Add +from OCC.Core.TopExp import TopExp_Explorer +from OCC.Core.TopoDS import topods_Face +from OCC.Core.gp import gp_Pnt +from OCC.Core.TopExp import TopExp_Explorer +from OCC.Core.TopAbs import TopAbs_EDGE, TopAbs_FACE, TopAbs_WIRE, TopAbs_SHELL, TopAbs_FORWARD, TopAbs_SOLID, TopAbs_COMPOUND + +def get_face_center_of_mass(face: TopoDS_Face, gp_pnt: bool = False) -> tuple: + """ + @brief Get the center of mass of a TopoDS_Face. This is useful for determining the center of mass of a face or to get the centre of mass of an object's surface. + @param face TopoDS_Face to get the center of mass of + @param gp_pnt If True return an gp_Pnt object otherwise a tuple of coordinates. + """ + props = GProp_GProps() + brepgprop_SurfaceProperties(face, props) + face_surf = props.CentreOfMass() + # face_surf. Coord if gp_pnt returns the face surface. + if gp_pnt: + return face_surf + else: + return face_surf.Coord() + +def get_volume_center_of_mass(vol: TopoDS_Solid, gp_pnt: bool = False): + """ + @brief Return the center of mass of a volume. This is an approximation of the centroid of the volume. + @param vol TopoDS_Solid object representing a volume + @param gp_pnt If True return an gp_Pnt object otherwise a tuple of coordinates. + @return Center of mass of a volume. + """ + props = GProp_GProps() + brepgprop_VolumeProperties(vol, props) + cog = props.CentreOfMass() + # Return the current coordinate of the current coordinate system. + if gp_pnt: + return cog + else: + return cog.Coord() + +def get_face_area(face: TopoDS_Face) -> float: + """ + @brief Get the area of a TopoDS_Face. This is an approximation of the area of the face. + @param face to get the area of. + @return The area of the face. + """ + props = GProp_GProps() + brepgprop_SurfaceProperties(face, props) + face_area = props.Mass() + return face_area + +def get_occ_bounding_box(shape: TopoDS_Shape) -> tuple: + """ + @brief Get bounding box of occupied space of topo shape. + @param shape TopoDS_Shape to be searched for occupied space + @return bounding box of occupied space in x y z coordinates + """ + bbox = Bnd_Box() + add_bbox = brepbndlib_Add(shape, bbox) + xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get() + return xmin, ymin, zmin, xmax, ymax, zmax + +def get_faces(_shape): + """ + @brief Get faces of a shape. This is a list of topods_Face objects that correspond to the faces of the shape + @param _shape shape to get faces of + @return list of topods_Face objects ( one for each face in the shape ) for each face + """ + topExp = TopExp_Explorer() + topExp.Init(_shape, TopAbs_FACE) + _faces = [] + + # Add faces to the faces list + while topExp.More(): + fc = topods_Face(topExp.Current()) + _faces.append(fc) + topExp.Next() + + return _faces + +def point_coord(p: gp_Pnt) -> tuple: + """ + @brief Returns the coord of a point. This is useful for debugging and to get the coordinates of an object that is a part of a geometry. + @param p gp_Pnt to get the coord of + @return tuple of the coordinate of the point ( x y z ) or None if not a point ( in which case the coordinates are None + """ + return p.Coord() + +def topo_explorer(shape: TopoDS_Shape, shape_type: str) -> list: + """ + @brief TopoDS Explorer for shape_type. This is a wrapper around TopExp_Explorer to allow more flexibility in the explorer + @param shape TopoDS_Shape to be explored. + @param shape_type Type of shape e. g. wire face shell solid compound edge + @return List of TopoDS_Shape that are explored by shape_type. Example : [ TopoDS_Shape ( " face " ) TopoDS_Shape ( " shell " + """ + result = [] + map_type = {"wire": TopAbs_WIRE, + "face": TopAbs_FACE, + "shell": TopAbs_SHELL, + "solid": TopAbs_SOLID, + "compound": TopAbs_COMPOUND, + "edge": TopAbs_EDGE} + explorer = TopExp_Explorer(shape, map_type[shape_type]) + # This method is called by the explorer. + while explorer.More(): + result.append(explorer.Current()) + explorer.Next() + return result \ No newline at end of file diff --git a/amworkflow/src/geometries/simple_geometry.py b/amworkflow/src/geometries/simple_geometry.py index 67527d9..0552736 100644 --- a/amworkflow/src/geometries/simple_geometry.py +++ b/amworkflow/src/geometries/simple_geometry.py @@ -1,23 +1,47 @@ from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox -from OCC.Core.TopoDS import TopoDS_Shape, TopoDS_Wire, TopoDS_Shell +from OCC.Core.TopoDS import TopoDS_Shape, TopoDS_Wire, TopoDS_Shell, TopoDS_Solid, TopoDS_Face, TopoDS_Edge, topods_Compound +from OCC.Core.Geom import Geom_TrimmedCurve from OCC.Core.gp import gp_Pnt, gp_Vec, gp_Ax2, gp_Dir -from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeEdge, BRepBuilderAPI_MakeWire, BRepBuilderAPI_MakeFace, BRepBuilderAPI_Sewing, BRepBuilderAPI_MakeSolid, BRepBuilderAPI_MakeShell, brepbuilderapi_Precision +from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeEdge, BRepBuilderAPI_MakeWire, BRepBuilderAPI_MakeFace, BRepBuilderAPI_Sewing, BRepBuilderAPI_MakeSolid, BRepBuilderAPI_MakeShell, brepbuilderapi_Precision,BRepBuilderAPI_MakePolygon from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakePrism, BRepPrimAPI_MakeCylinder from amworkflow.src.geometries.operator import geom_copy, translate, reverse -from amworkflow.src.geometries.builder import geometry_builder +from amworkflow.src.geometries.builder import geometry_builder, sewer from OCC.Core.GC import GC_MakeArcOfCircle import math as m - +from OCC.Core.BRepOffsetAPI import BRepOffsetAPI_MakeOffsetShape +from OCCUtils.Topology import Topo +import numpy as np def create_box(length: float, width: float, height: float, radius: float = None, - alpha: float = None) -> TopoDS_Shape: + alpha: float = None, + shell: bool = False) -> TopoDS_Shape: + """ + @brief Create a box with given length width height and radius. If radius is None or 0 the box will be sewed by a solid. + @param length Length of the box in points + @param width Width of the box. + @param height Height of the box. + @param radius Radius of the box. Default is None which means that the box is without curves. + @param alpha defines the angle of bending the box. Default is half the length divided by the radius. + @param shell If True the box will be shell. Default is False. + @return TopoDS_Shape with box in it's topolar form. Note that this is a Shape + """ if (radius == None) or (radius == 0): - return BRepPrimAPI_MakeBox(length, width, height).Shape() + if shell: + box = BRepPrimAPI_MakeBox(length, width, height).Shape() + faces = list(Topo(TopoDS_Solid).faces_from_solids(box)) + print(isinstance(faces[0], TopoDS_Shape)) + sewed_face = sewer(faces) + # make_shell = BRepBuilderAPI_MakeShell(sewed_face, False).Shell() + + return sewed_face + else: + return BRepPrimAPI_MakeBox(length, width, height).Shape() else: + # The alpha of the circle. if alpha == None: alpha = (length / radius)% (m.pi * 2) R = radius + (width / 2) @@ -37,11 +61,12 @@ def create_box(length: float, wire = BRepBuilderAPI_MakeWire(arch_edge1_2, edge2, arch_edge3_4, edge4).Wire() wire_top = geom_copy(wire) translate(wire_top, [0, 0, height]) - prism = create_prism(wire, [0, 0, height], True).Shape() + prism = create_prism(wire, [0, 0, height], True) bottom_face = create_face(wire) top_face = reverse(create_face(wire_top)) component = [prism, top_face, bottom_face] sewing = BRepBuilderAPI_Sewing() + # Add all components to the sewing. for i in range(len(component)): sewing.Add(component[i]) sewing.Perform() @@ -49,18 +74,167 @@ def create_box(length: float, # shell = BRepBuilderAPI_MakeShell(sewed_shape) solid = BRepBuilderAPI_MakeSolid(sewed_shape).Shape() curve_box = geometry_builder(component) - return solid + # Returns the shape of the shell. + if shell: + return sewed_shape + else: + return solid def create_cylinder(radius: float, length: float) -> TopoDS_Shape: + """ + @brief Create a cylinder shape. This is a convenience function for BRepPrimAPI_MakeCylinder + @param radius Radius of the cylinder in metres + @param length Length of the cylinder in metres. + @return Shape of the cylinder ( TopoDS_Shape ) that is created and ready to be added to topology + """ return BRepPrimAPI_MakeCylinder(radius, length).Shape() -def create_prism(wire: TopoDS_Wire, +def create_prism(shape: TopoDS_Shape, vector: list, - copy: bool): - return BRepPrimAPI_MakePrism(wire, gp_Vec(vector[0], + copy: bool = True) -> TopoDS_Shell: + """ + @brief Create prism from TopoDS_Shape and vector. It is possible to copy the based wire(s) if copy is True. I don't know what if it's False so it is recommended to always use True. + @param shape TopoDS_Shape to be used as base + @param vector list of 3 elements ( x y z ). Normally only use z to define the height of the prism. + @param copy boolean to indicate if the shape should be copied + @return return the prism + """ + return BRepPrimAPI_MakePrism(shape, gp_Vec(vector[0], vector[1], vector[2]), - copy) + copy).Shape() -def create_face(wire: TopoDS_Wire): +def create_face(wire: TopoDS_Wire) -> TopoDS_Face: + """ + @brief Create a BRep face from a TopoDS_Wire. This is a convenience function to use : func : ` BRepBuilderAPI_MakeFace ` and + @param wire The wire to create a face from. Must be a TopoDS_Wire. + @return A Face object with the properties specified + """ return BRepBuilderAPI_MakeFace(wire).Face() + +def create_wire(*edge) -> TopoDS_Wire: + """ + @brief Create a wire. Input at least one edge to build a wire. This is a convenience function to call BRepBuilderAPI_MakeWire with the given edge and return a wire. + @return A wire built from the given edge ( s ). The wire may be used in two ways : 1 + """ + return BRepBuilderAPI_MakeWire(*edge).Wire() + +def create_edge(pnt1: gp_Pnt = None, pnt2: gp_Pnt = None, arch: Geom_TrimmedCurve = None) -> TopoDS_Edge: + """ + @brief Create an edge between two points. This is a convenience function to be used in conjunction with : func : ` BRepBuilderAPI_MakeEdge ` + @param pnt1 first point of the edge + @param pnt2 second point of the edge + @param arch arch edge ( can be None ). If arch is None it will be created from pnt1 and pnt2 + @return an edge. + """ + if isinstance(pnt1, gp_Pnt) and isinstance(pnt2, gp_Pnt): + edge = BRepBuilderAPI_MakeEdge(pnt1, pnt2).Edge() + elif isinstance(arch, Geom_TrimmedCurve): + edge = BRepBuilderAPI_MakeEdge(arch).Edge() + return edge + +def create_arch(pnt1, pnt2, pnt1_2, make_edge: bool = True) -> TopoDS_Edge: + """ + @brief Create an arc of circle. If make_edge is True the arc is created in TopoDS_Edge. + @param pnt1 The first point of the arc. + @param pnt2 The second point of the arc. + @param pnt1_2 The intermediate point of the arc. + @param make_edge If True the arc is created in the x - y plane. + @return arch : return an ` GC_MakeArcOfCircle` object or an edge + """ + arch = GC_MakeArcOfCircle(pnt1, pnt1_2, pnt2).Value() + # Create an edge if make_edge is true. + if make_edge: + return create_edge(arch) + else: + return arch + +def create_wire_by_points(points: list): + """ + @brief Create a closed wire (loop) by points. The wire is defined by a list of points which are connected by an edge. + @param points A list of points. Each point is a gp_Pnt ( x y z) where x, y and z are the coordinates of a point. + @return A wire with the given points connected by an edge. This will be an instance of : class : `BRepBuilderAPI_MakeWire` + """ + pts = points + # Create a wire for each point in the list of points. + for i in range(len(pts)): + # Create a wire for the i th point. + if i == 0: + edge = create_edge(pts[i],pts[i+1]) + wire = create_wire(edge) + # Create a wire for the given points. + if i != len(pts)-1: + edge = create_edge(pts[i],pts[i+1]) + wire = create_wire(wire, edge) + else: + edge = create_edge(pts[i],pts[0]) + wire = create_wire(wire, edge) + return wire + +def random_polygon_constructor(points:list, isface: bool = True) -> TopoDS_Face or TopoDS_Wire: + """ + @brief Creates a polygon in any shape. If isface is True the polygon is made face - oriented otherwise it is wires + @param points List of points defining the polygon + @param isface True if you want to create a face - oriented + @return A polygon + """ + pb = BRepBuilderAPI_MakePolygon() + # Add points to the points. + for pt in points: + pb.Add(pt) + pb.Build() + pb.Close() + # Create a face or a wire. + if isface: + return create_face(pb.Wire()) + else: + return pb.Wire() + +def angle_of_two_arrays(a1:np.ndarray, a2:np.ndarray, rad: bool = True) -> float: + """ + @brief Returns the angle between two vectors. This is useful for calculating the rotation angle between a vector and another vector + @param a1 1D array of shape ( n_features ) + @param a2 2D array of shape ( n_features ) + @param rad If True the angle is in radians otherwise in degrees + @return Angle between a1 and a2 in degrees or radians depending on rad = True or False + """ + dot = np.dot(a1, a2) + norm = np.linalg.norm(a1)*np.linalg.norm(a2) + if rad: + return np.arccos(dot / norm) + else: + return np.rad2deg(np.arccos(dot / norm)) + +def laterality_indicator(a: np.ndarray, d:bool): + """ + @brief Compute laterality indicator of a vector. This is used to create a vector which is perpendicular to the based vector on its left side ( d = True ) or right side ( d = False ) + @param a vector ( a ) + @param d True if on left or False if on right + @return A vector. + """ + z = np.array([0,0,1]) + # cross product of z and a + if d: + na = np.cross(z,a) + else: + na = np.cross(-z,a) + norm = np.linalg.norm(na, na.shape[0]) + return na / norm + +def angular_bisector(a1:np.ndarray, a2:np.ndarray) -> np.ndarray: + """ + @brief Angular bisector between two vectors. The result is a vector splitting the angle between two vectors uniformly. + @param a1 1xN numpy array + @param a2 1xN numpy array + @return the bisector vector + """ + norm1 = np.linalg.norm(a1) + norm2 = np.linalg.norm(a2) + bst = a1 / norm1 + a2 / norm2 + norm3 = np.linalg.norm(bst) + # The laterality indicator a2 norm3 norm3 + if norm3 == 0: + opt = laterality_indicator(a2,True) + else: + opt = bst / norm3 + return opt diff --git a/amworkflow/src/infrastructure/fastapi/.gitkeep b/amworkflow/src/infrastructure/fastapi/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/amworkflow/src/interface/cli/cli_workflow.py b/amworkflow/src/interface/cli/cli_workflow.py index 3310ba1..79f4427 100644 --- a/amworkflow/src/interface/cli/cli_workflow.py +++ b/amworkflow/src/interface/cli/cli_workflow.py @@ -13,7 +13,7 @@ def cli(): parser.add_argument("-stlld", "--stl_linear_deflect", nargs="?", type=float) parser.add_argument("-n", "--name", nargs="?", type=str) parser.add_argument("-y", "--yaml_dir", nargs="?", type=str) - parser.add_argument("-s", "--step_dir", nargs="?", type=str) + parser.add_argument("-impt", "--import_dir", nargs="?", type=str) parser.add_argument("-e", "--edit", action="store_true") parser.add_argument("-rm", "--remove", action="store_true") args = parser.parse_args() diff --git a/amworkflow/src/utils/download.py b/amworkflow/src/utils/download.py index 2066aa5..1c0c0a7 100644 --- a/amworkflow/src/utils/download.py +++ b/amworkflow/src/utils/download.py @@ -22,14 +22,14 @@ def downloader(batch_num: int | str = None, except Exception as e: print(f"Unexpected error occurred: {e}") if batch_num != None: - result = query_multi_data(GeometryFile, str(batch_num)) + result = query_multi_data(GeometryFile, str(batch_num), "batch_num") new_path = D.USECASE_PATH_PARAMWALL_PATH.value + batch_num + "/" if not os.path.exists(new_path): os.mkdir(new_path) for row in result: - _result = row[0].__dict__ + _result = row filename = _result["filename"] - hash_name = _result["stl_hashname"] + hash_name = _result["geom_hashname"] copyer(src_file= file_lib + hash_name + ".stl", dest_file= new_path + filename) db_list = [XdmfFile, H5File] diff --git a/amworkflow/src/utils/parser.py b/amworkflow/src/utils/parser.py index 1d58ca2..0e21b03 100644 --- a/amworkflow/src/utils/parser.py +++ b/amworkflow/src/utils/parser.py @@ -36,31 +36,22 @@ def dict_flat(input_dict: dict) -> dict: # data_mapper(yaml_parser(D.USECASE_PATH_PARAMWALL_PATH.value, "test1.yaml")) # print(args.batch) # print(yaml_parser(D.USECASE_PATH_PARAMWALL_PATH.value, "test1.yaml")[L.BATCH_PARAM.value][L.IS_BATCH.value]) - def cmd_parser(args) -> dict: - opt = {"name": args.name, - L.GEOM_PARAM.value: {} - } + opt = {} if args.iter_param != None: it_param = np.array([args.iter_param[i:i+3] for i in range(0,len(args.iter_param),3)]).T - print(it_param) print(args.geom_param) opt[L.BATCH_PARAM.value] = {L.IS_BATCH.value: True} - for ind, item in enumerate(args.geom_param): - opt[L.GEOM_PARAM.value][item]= {L.STARTPOINT.value: args.geom_param_value[ind], - L.ENDPOINT.value: None, - L.NUM.value: None} - if ind + 1 in it_param[0]: - opt[L.GEOM_PARAM.value][item].update({L.ENDPOINT.value:it_param[1][ind],L.NUM.value:it_param[2][ind]}) - if args.mesh_by_layer != None: - opt[L.MESH_PARAM.value] = {L.LYR_NUM.value:args.mesh_by_layer} - if args.mesh_by_thickness != None: - opt[L.MESH_PARAM.value] = {L.LYR_TKN:args.mesh_by_thickness} - if args.mesh_size_factor != None: - opt[L.MESH_PARAM.value].update({"mesh_size_factor":args.mesh_size_factor}) - if args.stl_linear_deflect != None: - opt[L.STL_PARAM.value] = {"linear_deflection":args.stl_linear_deflect} - if args.stl_angular_deflect != None: - opt[L.STL_PARAM.value].update({"angular_deflection":args.stl_angular_deflect}) + else: + opt[L.BATCH_PARAM.value] = {L.IS_BATCH.value: False} + if args.geom_param != None: + opt[L.GEOM_PARAM.value] = {} + for ind, item in enumerate(args.geom_param): + opt[L.GEOM_PARAM.value][item]= {L.STARTPOINT.value: args.geom_param_value[ind], + L.ENDPOINT.value: None, + L.NUM.value: None} + if args.iter_param != None: + if ind + 1 in it_param[0]: + opt[L.GEOM_PARAM.value][item].update({L.ENDPOINT.value:it_param[1][ind],L.NUM.value:it_param[2][ind]}) return opt diff --git a/amworkflow/src/utils/reader.py b/amworkflow/src/utils/reader.py index 3e8025a..64dbba1 100644 --- a/amworkflow/src/utils/reader.py +++ b/amworkflow/src/utils/reader.py @@ -1,5 +1,5 @@ import os -from OCC.Extend.DataExchange import read_step_file +from OCC.Extend.DataExchange import read_step_file, read_stl_file from OCC.Core.TopoDS import TopoDS_Shape def get_filename(path: str) -> str: @@ -7,3 +7,6 @@ def get_filename(path: str) -> str: def step_reader(path: str) -> TopoDS_Shape(): return read_step_file(filename=path) + +def stl_reader(path: str) -> TopoDS_Shape: + return read_stl_file(filename=path) \ No newline at end of file diff --git a/amworkflow/src/utils/sanity_check.py b/amworkflow/src/utils/sanity_check.py index 2b6fe5e..e532724 100644 --- a/amworkflow/src/utils/sanity_check.py +++ b/amworkflow/src/utils/sanity_check.py @@ -3,6 +3,10 @@ import os def import_freecad_check(): + """ + @brief Check if freecad is installed and if so import FreeCAD. This is needed to avoid importing FREECAD in order to be able to run a part program that is not available on the system. + @return True if freecad is installed False if not or error occurs during import of the freecad + """ import sys from amworkflow.src.constants.enums import Directory freecad_path = Directory.SYS_PATH.value + Directory.FREECAD_PATH.value @@ -15,7 +19,13 @@ def import_freecad_check(): return False def dimension_check(dm_list: list): + """ + @brief Checks if the dimensions are correct. This is a helper function to make sure that the dimensions are in the correct order + @param dm_list List of dimensions + """ + # Checks the width and length of the list of DM items. for dm_item in dm_list: + # Check if the dimensions of the item are within the radius. if dm_item[3] != 0 or None: try: assert(dm_item[3] - 0.5 * dm_item[1] > 0) @@ -27,6 +37,9 @@ def dimension_check(dm_list: list): raise DimensionViolationException("Length is too large.") def path_append_check(): + """ + @brief Append path to sys. path if source files are not available. This is necessary to avoid import errors + """ try: import src except: @@ -36,11 +49,21 @@ def path_append_check(): sys.path.append(op.dirname(op.dirname(__file__))) def path_valid_check(path: str, format: list) -> bool: - try: - os.path.isdir(path) - except: + """ + @brief Check if path is valid and return file name if not raise InvalidFileFormatException. This is used to check if file can be read from file system + @param path path to file or directory + @param format list of file formats to check if file is in + @return filename's extension or False if file is not in format ( no extension ) or file is not in + """ + split_result = path.rsplit("/", 1) + # Split the result of a split command. + if len(split_result) > 1: + dir_path, filename = split_result + # if dir_path is not a directory + if os.path.isdir(dir_path) == False: raise AssertionError("wrong path provided") - try: - path[-3:] in format or path[-4:] in format - except: - raise InvalidFileFormatException(item=path[-4:]) + # If filename is not in format raise InvalidFileFormatException item filename + if (filename[-3:] not in format) and (filename[-4:] not in format): + raise InvalidFileFormatException(item=filename[-4:]) + else: + return filename[-3:] diff --git a/amworkflow/src/utils/visualizer.py b/amworkflow/src/utils/visualizer.py index cd1af23..a7efeeb 100644 --- a/amworkflow/src/utils/visualizer.py +++ b/amworkflow/src/utils/visualizer.py @@ -6,4 +6,5 @@ def mesh_visualizer(): try: gmsh.is_initialized() except: - raise GmshUseBeforeInitializedException() \ No newline at end of file + raise GmshUseBeforeInitializedException() + gmsh.fltk.run() \ No newline at end of file diff --git a/amworkflow/src/utils/writer.py b/amworkflow/src/utils/writer.py index e045544..db28a30 100644 --- a/amworkflow/src/utils/writer.py +++ b/amworkflow/src/utils/writer.py @@ -18,12 +18,23 @@ def stl_writer(item: any, item_name: str, linear_deflection: float = 0.001, angular_deflection: float = 0.1, output_mode = 1, store_dir: str = None) -> None: + """ + @brief Write OCC to STL file. This function is used to write a file to the database. The file is written to a file named item_name. + @param item the item to be written to the file. + @param item_name the name of the item. It is used to generate the file name. + @param linear_deflection the linear deflection factor. + @param angular_deflection the angular deflection factor. + @param output_mode for using different api in occ. + @param store_dir the directory to store the file in. + @return None if success else error code ( 1 is returned if error ). In case of error it is possible to raise an exception + """ match output_mode: case 0: logging.info("Using stlAPI_Writer now...") stl_write = StlAPI_Writer() stl_write.SetASCIIMode(True) # Set to False for binary STL output status = stl_write.Write(item, item_name) + # if status is set to true then the status is not done. if status: logging.info("Done!") case 1: @@ -38,6 +49,11 @@ def stl_writer(item: any, item_name: str, linear_deflection: float = 0.001, angu ) def step_writer(item: any, filename: str): + """ + @brief Writes a step file. This is a wrapper around write_step_file to allow a user to specify the shape of the step and a filename + @param item the item to write to the file + @param filename the filename to write the file to ( default is None + """ result = write_step_file(a_shape= item, filename= filename) @@ -49,8 +65,21 @@ def namer(name_type: str, layer_param: float or int = None, geom_name: str = None ) -> str: + """ + @brief Generate a name based on the type of name. It is used to generate an output name for a layer or a geometric object + @param name_type Type of name to generate + @param dim_vector Vector of dimension values ( default : None ) + @param batch_num Number of batch to generate ( default : None ) + @param parm_title List of parameters for the layer + @param is_layer_thickness True if the layer is thickness ( default : False ) + @param layer_param Parameter of the layer ( default : None ) + @param geom_name Name of the geometric object ( default : None ) + @return Name of the layer or geometric object ( default : None ) - The string representation of the nam + """ + # Title of the parameter list. if parm_title != None: title = [[j for j in i][0].upper() for i in parm_title] + # Replace. with _. if layer_param != None: layer_param = str(layer_param).replace(".", "_") match name_type: @@ -68,6 +97,7 @@ def namer(name_type: str, output = "-".join([title[i] + repl_vector[i] for i in range(len(title))]) + "-" + str(batch_num) case "mesh": + # The layer thickness layer. if is_layer_thickness: output = f"MeLT{layer_param}-" + geom_name else: @@ -78,14 +108,33 @@ def namer(name_type: str, # print(namer("dimension", np.array([33.2, 22.44, 55.3, 66.8]))) def batch_num_creator(): + """ + @brief Create batch number for current date and time. It is used to determine how many batches are created in one batch. + @return datetime. datetime date and time in YYYYMMDDHHMMSS format with format'%Y%m%d + """ return datetime.now().strftime(T.YY_MM_DD_HH_MM_SS.value) def vtk_writer(item: any, dirname: str, filename: str) -> None: + """ + @brief Write a VTK file. This is a wrapper around the : py : func : ` ~voxel. write ` function that prepends the dirname and filename to the file name. + @param item The item to write. It must be a + @param dirname The directory to write the file to + @param filename The filename to write to + @return True if the write was successful False otherwise. >>> import numpy as np >>> vtk_writer = np. fromfile ( " test. vtk " + """ item.write(dirname + filename) def mesh_writer(item: gmsh.model, directory: str, filename: str, output_filename: str, format: str): + """ + @brief Writes mesh to file. This function is used to write meshes to file. The format is determined by the value of the format parameter + @param item gmsh. model object that contains the model + @param directory directory where the file is located. It is the root of the file + @param filename name of the file to be written + @param output_filename name of the file to be written + @param format format of the file to be written. Valid values are vtk msh + """ try: gmsh.is_initialized() except: @@ -93,8 +142,10 @@ def mesh_writer(item: gmsh.model, directory: str, filename: str, output_filename item.set_current(filename) phy_gp = item.getPhysicalGroups() model_name = item.get_current() + # Write the format to the file. if format == "vtk" or format == "msh": gmsh.write(directory + filename + "." + format) + # Create a mesh file for the current model. if format == "xdmf": msh, cell_markers, facet_markers = gmshio.model_to_mesh(item, MPI.COMM_SELF, 0) msh.name = item.get_current() diff --git a/amworkflow/tests/test.py b/amworkflow/tests/test.py index e69de29..82165a0 100644 --- a/amworkflow/tests/test.py +++ b/amworkflow/tests/test.py @@ -0,0 +1,63 @@ +import warnings +warnings.filterwarnings("ignore", category=DeprecationWarning) +import re +from amworkflow.src.constants.data_model import MapParamModel +from amworkflow.src.utils.writer import namer +from amworkflow.src.geometries.simple_geometry import create_box +from OCC.Core.TopoDS import TopoDS_Solid, TopoDS_Face, TopoDS_Compound +from amworkflow.src.geometries.operator import intersector +from amworkflow.src.constants.data_model import DeepMapParamModel, MapParamModel +from amworkflow.src.utils.sanity_check import path_valid_check + +# Writer +def is_hex(string): + hex_pattern = r'^[0-9a-fA-F]+$' + return re.match(hex_pattern, string) is not None and len(string) == 32 + +def test_data_model_with_init_data(): + label = ["a", "b", "c"] + data = [1,2,3] + model = MapParamModel(label=label, data=data) + assert model.a ==1, "should be 1" + + +def test_data_model_without_init_data(): + label = ["a", "b", "c"] + model = MapParamModel(label=label) + model.a = 1 + assert model.a ==1, "should be 1" + +def test_namer_hex(): + assert is_hex(namer(name_type="hex")) + +def test_namer_dimension_and_batch(): + title = ["len", "height", "width"] + data = [2.2, 3.1, 44] + name = "L2_2-H3_1-W44-20230101" + batch_num = 20230101 + assert namer(name_type="dimension-batch",dim_vector=data, parm_title=title), title + +def test_name_mesh(): + assert namer(name_type="mesh", layer_param=0.2, geom_name="test"), "MeLT0.2-test" + +def test_makebox(): + box = create_box(1,2,3) + assert isinstance(box, TopoDS_Solid) + +def test_intersector(): + com = intersector(create_box(1,2,3), 1.3, "z") + assert isinstance(com, TopoDS_Face) + +def test_dpdm(): + tdic = {"A":{"a":1}, + "B":{"b":2, + "ba": 3}, + "C":{"c":{"ca":4}}} + dmpm = DeepMapParamModel(tdic) + assert dmpm.C.c.ca == 4 + +def test_pdm(): + lab = ["a", "b", "c"] + val = [2, 3, 1] + dat = MapParamModel(lab,val) + assert dat.b == 3 diff --git a/amworkflow/tests/test_base_workflow.py b/amworkflow/tests/test_base_workflow.py new file mode 100644 index 0000000..e23ccfd --- /dev/null +++ b/amworkflow/tests/test_base_workflow.py @@ -0,0 +1,36 @@ +from amworkflow.src.geometries.operator import intersector +from amworkflow.src.geometries.simple_geometry import create_box +from amworkflow.src.utils.writer import stl_writer +from OCC.Core.TopoDS import TopoDS_Compound, TopoDS_Iterator +from OCC.Core.TopExp import TopExp_Explorer +from OCC.Core.TopAbs import TopAbs_ShapeEnum +from OCC.Core.TopoDS import TopoDS_Face +from OCC.Core.BRepTools import BRepTools_WireExplorer +from OCC.Core.BRepGProp import brepgprop_VolumeProperties, brepgprop_SurfaceProperties +import OCC.Core.gp as gp +from OCC.Core.GProp import GProp_GProps +com = [intersector(create_box(1,2,3, 80), 1.3, "z")] +# stl_writer(com, "intersct") + + + +# Iterate through the subshapes in the compound +def subshape(shape_list): + for shape in shape_list: + iterator = TopoDS_Iterator(shape) + while iterator.More(): + subshape = iterator.Value() + + # Do something with the subshape + # For example, you can print its shape type + shape_type = subshape.ShapeType() + print(f"Shape type: {shape_type}") + opt_slist = [] + opt_slist.append(subshape) + + # Move to the next subshape + iterator.Next() + return opt_slist + + + diff --git a/environment.yaml b/environment.yaml index 40d0135..a1f1540 100644 --- a/environment.yaml +++ b/environment.yaml @@ -5,48 +5,52 @@ channels: dependencies: - _libgcc_mutex=0.1=conda_forge - _openmp_mutex=4.5=2_gnu - - aiohttp=3.8.4=py310h1fa729e_0 - - aiosignal=1.3.1=pyhd8ed1ab_0 + - _sysroot_linux-64_curr_repodata_hack=3=haa98f57_10 + - aiohttp=3.8.3=py310h5eee18b_0 + - aiosignal=1.2.0=pyhd3eb1b0_0 - alsa-lib=1.2.8=h166bdaf_0 - aom=3.5.0=h27087fc_0 - - asttokens=2.2.1=pyhd8ed1ab_0 - - async-timeout=4.0.2=pyhd8ed1ab_0 - - atk-1.0=2.38.0=hd4edc92_1 + - appdirs=1.4.4=pyhd3eb1b0_0 + - asttokens=2.0.5=pyhd3eb1b0_0 + - async-timeout=4.0.2=py310h06a4308_0 + - atk-1.0=2.36.0=ha1a6a79_0 - attr=2.5.1=h166bdaf_1 - - attrs=23.1.0=pyh71513ae_1 - - backcall=0.2.0=pyh9f0ad1d_0 - - backports=1.0=pyhd8ed1ab_3 - - backports.functools_lru_cache=1.6.4=pyhd8ed1ab_0 - - binutils_impl_linux-64=2.39=he00db2b_1 - - binutils_linux-64=2.39=h5fc0e48_13 - - blosc=1.21.4=h0f2a231_0 + - attrs=22.1.0=py310h06a4308_0 + - backcall=0.2.0=pyhd3eb1b0_0 + - backports=1.1=pyhd3eb1b0_0 + - backports.functools_lru_cache=1.6.4=pyhd3eb1b0_0 + - binutils_impl_linux-64=2.38=h2a08ee3_1 + - binutils_linux-64=2.38.0=hc2dff05_0 + - blas=1.0=openblas + - blosc=1.21.3=h6a678d5_0 - boost-cpp=1.78.0=h5adbc97_2 - - brotli=1.0.9=h166bdaf_8 - - brotli-bin=1.0.9=h166bdaf_8 - - brotlipy=0.7.0=py310h5764c6d_1005 - - bzip2=1.0.8=h7f98852_4 - - c-ares=1.19.0=hd590300_0 + - bottleneck=1.3.5=py310ha9d4c09_0 + - brotli=1.0.9=h5eee18b_7 + - brotli-bin=1.0.9=h5eee18b_7 + - brotlipy=0.7.0=py310h7f8727e_1002 + - bzip2=1.0.8=h7b6447c_0 + - c-ares=1.19.0=h5eee18b_0 - ca-certificates=2023.05.30=h06a4308_0 - - cached-property=1.5.2=hd8ed1ab_1 + - cached-property=1.5.2=py_0 - cached_property=1.5.2=pyha770c72_1 - cairo=1.16.0=ha61ee94_1014 - certifi=2023.5.7=py310h06a4308_0 - - cffi=1.15.1=py310h255011f_3 - - cftime=1.6.2=py310hde88566_1 - - charset-normalizer=2.1.1=pyhd8ed1ab_0 - - cloudpickle=2.2.1=pyhd8ed1ab_0 - - colorama=0.4.6=pyhd8ed1ab_0 - - contourpy=1.0.7=py310hdf3cbec_0 - - cryptography=40.0.2=py310h34c0648_0 + - cffi=1.15.1=py310h5eee18b_3 + - cftime=1.6.2=py310ha9d4c09_0 + - charset-normalizer=2.0.4=pyhd3eb1b0_0 + - cloudpickle=2.2.1=py310h06a4308_0 + - colorama=0.4.6=py310h06a4308_0 + - contourpy=1.0.5=py310hdb19cb5_0 + - cryptography=39.0.1=py310h9ce1e76_2 - curl=8.1.0=h409715c_0 - - cycler=0.11.0=pyhd8ed1ab_0 - - dbus=1.13.6=h5008d03_3 - - decorator=5.1.1=pyhd8ed1ab_0 - - doit=0.36.0=pyhd8ed1ab_0 + - cycler=0.11.0=pyhd3eb1b0_0 + - dbus=1.13.18=hb2f20db_0 + - decorator=5.1.1=pyhd3eb1b0_0 + - doit=0.33.1=py310h06a4308_0 - double-conversion=3.2.0=h27087fc_1 - - eigen=3.4.0=h4bd325d_0 - - exceptiongroup=1.1.1=pyhd8ed1ab_0 - - executing=1.2.0=pyhd8ed1ab_0 + - eigen=3.3.7=hd09550d_1 + - exceptiongroup=1.0.4=py310h06a4308_0 + - executing=0.8.3=pyhd3eb1b0_0 - expat=2.5.0=hcb278e6_1 - fenics-basix=0.6.0=py310hdf3cbec_0 - fenics-dolfinx=0.6.0=py310hf97048e_101 @@ -57,248 +61,254 @@ dependencies: - fenics-ufl=2023.1.1=pyhd8ed1ab_1 - ffmpeg=5.1.2=gpl_h8dda1f0_106 - fftw=3.3.10=mpi_mpich_h5537406_7 - - font-ttf-dejavu-sans-mono=2.37=hab24e00_0 - - font-ttf-inconsolata=3.000=h77eed37_0 - - font-ttf-source-code-pro=2.038=h77eed37_0 - - font-ttf-ubuntu=0.83=hab24e00_0 + - font-ttf-dejavu-sans-mono=2.37=hd3eb1b0_0 + - font-ttf-inconsolata=2.001=hcb22688_0 + - font-ttf-source-code-pro=2.030=hd3eb1b0_0 + - font-ttf-ubuntu=0.83=h8b1ccd4_0 - fontconfig=2.14.2=h14ed4e7_0 - - fonts-conda-ecosystem=1=0 + - fonts-anaconda=1=h8fa9717_0 + - fonts-conda-ecosystem=1=hd3eb1b0_0 - fonts-conda-forge=1=0 - - fonttools=4.39.4=py310h2372a71_0 + - fonttools=4.25.0=pyhd3eb1b0_0 - freeimage=3.18.0=he1b5a44_0 - - freetype=2.12.1=hca18f0e_1 - - fribidi=1.0.10=h36c2ea0_0 - - frozenlist=1.3.3=py310h5764c6d_0 - - gcc_impl_linux-64=12.2.0=hcc96c02_19 - - gcc_linux-64=12.2.0=h4798a0e_13 - - gdk-pixbuf=2.42.10=h05c8ddd_0 + - freetype=2.12.1=h4a9f257_0 + - fribidi=1.0.10=h7b6447c_0 + - frozenlist=1.3.3=py310h5eee18b_0 + - gcc_impl_linux-64=11.2.0=h1234567_1 + - gcc_linux-64=11.2.0=h5c386dc_0 + - gdk-pixbuf=2.42.10=h5eee18b_0 - gettext=0.21.1=h27087fc_0 - - giflib=5.2.1=h0b41bf4_3 - - gl2ps=1.4.2=h0708190_0 - - glew=2.1.0=h9c3ff4c_2 + - giflib=5.2.1=h5eee18b_3 + - gl2ps=1.4.2=h70c0345_1 + - glew=2.1.0=h295c915_3 - glib=2.76.2=hfc55251_0 - glib-tools=2.76.2=hfc55251_0 - - gmp=6.2.1=h58526e2_0 + - gmp=6.2.1=h295c915_3 - gnutls=3.7.8=hf3e180e_0 - - graphite2=1.3.13=h58526e2_1001 - - graphviz=7.1.0=h2e5815a_0 + - gobject-introspection=1.72.0=py310hbb6d50b_2 + - graphite2=1.3.14=h295c915_1 + - graphviz=2.50.0=h1b29801_1 - gst-plugins-base=1.22.0=h4243ec0_2 - gstreamer=1.22.0=h25f0c4b_2 - gstreamer-orc=0.4.33=h166bdaf_0 - - gtk2=2.24.33=h90689f9_2 - - gts=0.7.6=h64030ff_2 - - gxx_impl_linux-64=12.2.0=hcc96c02_19 - - gxx_linux-64=12.2.0=hb41e900_13 + - gtk2=2.24.33=h73c1081_2 + - gts=0.7.6=hb67d8dd_3 + - gxx_impl_linux-64=11.2.0=h1234567_1 + - gxx_linux-64=11.2.0=hc2dff05_0 - h5py=3.8.0=nompi_py310h0311031_100 - harfbuzz=6.0.0=h8e241bc_0 - hdf4=4.2.15=h9772cbc_5 - hdf5=1.12.2=mpi_mpich_h5d83325_1 - hypre=2.25.0=mpi_mpich_hed3a557_0 - icu=70.1=h27087fc_0 - - idna=3.4=pyhd8ed1ab_0 - - importlib-metadata=6.6.0=pyha770c72_0 - - importlib_metadata=6.6.0=hd8ed1ab_0 - - iniconfig=2.0.0=pyhd8ed1ab_0 - - ipython=8.13.2=pyh41d4057_0 + - idna=3.4=py310h06a4308_0 + - importlib-metadata=6.0.0=py310h06a4308_0 + - importlib_metadata=6.0.0=hd3eb1b0_0 + - iniconfig=1.1.1=pyhd3eb1b0_0 + - ipython=8.12.0=py310h06a4308_0 - jack=1.9.22=h11f4161_0 - - jedi=0.18.2=pyhd8ed1ab_0 - - jpeg=9e=h0b41bf4_3 + - jedi=0.18.1=py310h06a4308_1 + - jpeg=9e=h5eee18b_1 - jsoncpp=1.9.5=h4bd325d_1 - - kernel-headers_linux-64=2.6.32=he073ed8_15 + - kernel-headers_linux-64=3.10.0=h57e8cba_10 - keyutils=1.6.1=h166bdaf_0 - - kiwisolver=1.4.4=py310hbf28c38_1 - - krb5=1.20.1=h81ceb04_0 - - lame=3.100=h166bdaf_1003 - - lcms2=2.15=hfd0df8a_0 - - ld_impl_linux-64=2.39=hcc3a1bd_1 - - lerc=4.0.0=h27087fc_0 + - kiwisolver=1.4.4=py310h6a678d5_0 + - krb5=1.20.1=h143b758_1 + - lame=3.100=h7b6447c_0 + - lcms2=2.12=h3be6417_0 + - ld_impl_linux-64=2.38=h1181459_1 + - lerc=3.0=h295c915_0 - libadios2=2.8.3=mpi_mpich_h781bc9b_5 - libaec=1.0.6=hcb278e6_1 - libblas=3.9.0=16_linux64_openblas - - libbrotlicommon=1.0.9=h166bdaf_8 - - libbrotlidec=1.0.9=h166bdaf_8 - - libbrotlienc=1.0.9=h166bdaf_8 + - libbrotlicommon=1.0.9=h5eee18b_7 + - libbrotlidec=1.0.9=h5eee18b_7 + - libbrotlienc=1.0.9=h5eee18b_7 - libcap=2.67=he9d0100_0 - libcblas=3.9.0=16_linux64_openblas - libclang=15.0.7=default_h7634d5b_2 - libclang13=15.0.7=default_h9986a30_2 - libcups=2.3.3=h36d4200_3 - libcurl=8.1.0=h409715c_0 - - libdb=6.2.32=h9c3ff4c_0 - - libdeflate=1.17=h0b41bf4_0 + - libdb=6.2.32=h6a678d5_1 + - libdeflate=1.17=h5eee18b_0 - libdrm=2.4.114=h166bdaf_0 - - libedit=3.1.20191231=he28a2e2_2 - - libev=4.33=h516909a_1 + - libedit=3.1.20221030=h5eee18b_0 + - libev=4.33=h7f8727e_1 - libevent=2.1.10=h28343ad_4 - libexpat=2.5.0=hcb278e6_1 - - libffi=3.4.2=h7f98852_5 + - libffi=3.4.4=h6a678d5_0 - libflac=1.4.2=h27087fc_0 - - libgcc-devel_linux-64=12.2.0=h3b97bd3_19 + - libgcc-devel_linux-64=11.2.0=h1234567_1 - libgcc-ng=12.2.0=h65d4601_19 - libgcrypt=1.10.1=h166bdaf_0 - - libgd=2.3.3=h5aea950_4 + - libgd=2.3.3=h695aa2c_1 - libgfortran-ng=12.2.0=h69a702a_19 - libgfortran5=12.2.0=h337968e_19 - libglib=2.76.2=hebfc3b9_0 - - libglu=9.0.0=he1b5a44_1001 + - libglu=9.0.0=hf484d3e_1 - libgomp=12.2.0=h65d4601_19 - libgpg-error=1.46=h620e276_0 - libhwloc=2.9.1=hd6dc26d_0 - libiconv=1.17=h166bdaf_0 - - libidn2=2.3.4=h166bdaf_0 + - libidn2=2.3.4=h5eee18b_0 - liblapack=3.9.0=16_linux64_openblas - libllvm15=15.0.7=hadd5161_1 - libnetcdf=4.9.1=mpi_mpich_hf10a581_1 - - libnghttp2=1.52.0=h61bc06f_0 - - libnsl=2.0.0=h7f98852_0 - - libogg=1.3.4=h7f98852_1 - - libopenblas=0.3.21=pthreads_h78a6416_3 - - libopus=1.3.1=h7f98852_1 + - libnghttp2=1.52.0=h2d74bed_1 + - libnsl=2.0.0=h5eee18b_0 + - libogg=1.3.5=h27cfd23_1 + - libopenblas=0.3.21=h043d6bf_0 + - libopus=1.3.1=h7b6447c_0 - libpciaccess=0.17=h166bdaf_0 - - libpng=1.6.39=h753d276_0 + - libpng=1.6.39=h5eee18b_0 - libpq=15.3=hbcd7760_1 - librsvg=2.54.4=h7abd40a_0 - libsanitizer=12.2.0=h46fd767_19 - libsndfile=1.2.0=hb75c966_0 - - libsodium=1.0.18=h36c2ea0_1 + - libsodium=1.0.18=h7b6447c_0 - libsqlite=3.42.0=h2797004_0 - - libssh2=1.10.0=hf14f497_3 - - libstdcxx-devel_linux-64=12.2.0=h3b97bd3_19 + - libssh2=1.10.0=hdbd6064_2 + - libstdcxx-devel_linux-64=11.2.0=h1234567_1 - libstdcxx-ng=12.2.0=h46fd767_19 - libsystemd0=253=h8c4010b_1 - - libtasn1=4.19.0=h166bdaf_0 - - libtheora=1.1.1=h7f98852_1005 - - libtiff=4.5.0=h6adf6a1_2 + - libtasn1=4.19.0=h5eee18b_0 + - libtheora=1.1.1=h7f8727e_3 + - libtiff=4.5.0=h6a678d5_2 - libtool=2.4.7=h27087fc_0 - libudev1=253=h0b41bf4_1 - - libunistring=0.9.10=h7f98852_0 + - libunistring=0.9.10=h27cfd23_0 - libuuid=2.38.1=h0b41bf4_0 - libva=2.18.0=h0b41bf4_0 - - libvorbis=1.3.7=h9c3ff4c_0 - - libvpx=1.11.0=h9c3ff4c_3 - - libwebp=1.2.4=h1daa5a0_1 - - libwebp-base=1.2.4=h166bdaf_0 + - libvorbis=1.3.7=h7b6447c_0 + - libvpx=1.11.0=h295c915_0 + - libwebp=1.2.4=h11a3e52_1 + - libwebp-base=1.2.4=h5eee18b_1 - libxcb=1.13=h7f98852_1004 - libxkbcommon=1.5.0=h79f4944_1 - libxml2=2.10.3=hca2bb57_4 - libzip=1.9.2=hc929e4a_1 - libzlib=1.2.13=h166bdaf_4 - - loguru=0.7.0=py310hff52083_0 - - lz4-c=1.9.4=hcb278e6_0 - - markdown-it-py=2.2.0=pyhd8ed1ab_0 - - matplotlib=3.7.1=py310hff52083_0 - - matplotlib-base=3.7.1=py310he60537e_0 - - matplotlib-inline=0.1.6=pyhd8ed1ab_0 - - mdurl=0.1.0=pyhd8ed1ab_0 + - loguru=0.5.3=py310h06a4308_4 + - lz4-c=1.9.4=h6a678d5_0 + - markdown-it-py=2.2.0=py310h06a4308_1 + - matplotlib=3.7.1=py310h06a4308_1 + - matplotlib-base=3.7.1=py310h1128e8f_1 + - matplotlib-inline=0.1.6=py310h06a4308_0 + - mdurl=0.1.0=py310h06a4308_0 - meshio=5.3.4=pyhd8ed1ab_0 - - metis=5.1.0=h58526e2_1006 - - mpfr=4.2.0=hb012696_0 + - metis=5.1.0=hf484d3e_4 + - mpfr=4.0.2=hb69a4c5_1 - mpg123=1.31.3=hcb278e6_0 - mpi=1.0=mpich - mpi4py=3.1.4=py310h37cc914_0 - mpich=4.0.3=h846660c_100 - - multidict=6.0.4=py310h1fa729e_0 + - multidict=6.0.2=py310h5eee18b_0 - mumps-include=5.2.1=ha770c72_11 - mumps-mpi=5.2.1=h7ee95aa_11 - - munkres=1.1.4=pyh9f0ad1d_0 + - munkres=1.1.4=py_0 - mysql-common=8.0.32=hf1915f5_2 - mysql-libs=8.0.32=hca2cd23_2 - - ncurses=6.3=h27087fc_1 + - ncurses=6.4=h6a678d5_0 - netcdf4=1.6.3=nompi_py310h0feb132_100 - nettle=3.8.1=hc379101_1 - - nlohmann_json=3.11.2=h27087fc_0 - - nspr=4.35=h27087fc_0 - - nss=3.89=he45b914_0 - - numpy=1.24.3=py310ha4c1d20_0 + - ninja=1.10.2=h06a4308_5 + - ninja-base=1.10.2=hd09550d_5 + - nlohmann_json=3.11.2=h6a678d5_0 + - nspr=4.35=h6a678d5_0 + - nss=3.89.1=h6a678d5_0 + - numexpr=2.8.4=py310h757a811_1 + - numpy=1.25.0=py310heeff2f4_0 + - numpy-base=1.25.0=py310h8a23956_0 - occt=7.7.0=h6cf717c_2 - openh264=2.3.1=hcb278e6_2 - - openjpeg=2.5.0=hfec8fc6_2 + - openjpeg=2.4.0=h3ad879b_0 - openssl=3.1.1=hd590300_1 - p11-kit=0.24.1=hc5aa10d_0 - - packaging=23.1=pyhd8ed1ab_0 - - pandas=2.0.1=py310h7cbd5c2_1 + - packaging=23.0=py310h06a4308_0 + - pandas=1.5.3=py310h1128e8f_0 - pango=1.50.14=hd33c08f_0 - parmetis=4.0.3=h2a9763c_1005 - - parso=0.8.3=pyhd8ed1ab_0 + - parso=0.8.3=pyhd3eb1b0_0 - pcre2=10.40=hc3806b6_0 - petsc=3.17.4=real_h15390b8_101 - petsc4py=3.17.4=real_hce8fbc3_101 - - pexpect=4.8.0=pyh1a96a4e_2 - - pickleshare=0.7.5=py_1003 - - pillow=9.4.0=py310h023d228_1 - - pip=23.1.2=pyhd8ed1ab_0 - - pixman=0.40.0=h36c2ea0_0 - - platformdirs=3.5.1=pyhd8ed1ab_0 - - pluggy=1.0.0=pyhd8ed1ab_5 - - ply=3.11=py_1 - - pooch=1.7.0=pyha770c72_3 + - pexpect=4.8.0=pyhd3eb1b0_3 + - pickleshare=0.7.5=pyhd3eb1b0_1003 + - pillow=9.4.0=py310h6a678d5_0 + - pip=23.1.2=py310h06a4308_0 + - pixman=0.40.0=h7f8727e_1 + - platformdirs=2.5.2=py310h06a4308_0 + - pluggy=1.0.0=py310h06a4308_1 + - ply=3.11=py310h06a4308_0 + - pooch=1.4.0=pyhd3eb1b0_0 + - poppler=22.12.0=h381b16e_0 + - poppler-data=0.4.11=h06a4308_1 - proj=9.1.1=h8ffa02c_2 - - prompt-toolkit=3.0.38=pyha770c72_0 - - prompt_toolkit=3.0.38=hd8ed1ab_0 - - pthread-stubs=0.4=h36c2ea0_1001 + - prompt-toolkit=3.0.36=py310h06a4308_0 + - prompt_toolkit=3.0.36=hd3eb1b0_0 + - pthread-stubs=0.3=h0ce48e5_1 - ptscotch=6.0.9=hb499603_2 - - ptyprocess=0.7.0=pyhd3deb0d_0 - - pugixml=1.11.4=h9c3ff4c_0 + - ptyprocess=0.7.0=pyhd3eb1b0_2 + - pugixml=1.11.4=h295c915_1 - pulseaudio=16.1=hcb278e6_3 - pulseaudio-client=16.1=h5195f5e_3 - pulseaudio-daemon=16.1=ha8d29e2_3 - - pure_eval=0.2.2=pyhd8ed1ab_0 - - pycparser=2.21=pyhd8ed1ab_0 - - pygments=2.15.1=pyhd8ed1ab_0 - - pygraphviz=1.10=py310h2ea6abb_3 - - pyopenssl=23.1.1=pyhd8ed1ab_0 - - pyparsing=3.0.9=pyhd8ed1ab_0 + - pure_eval=0.2.2=pyhd3eb1b0_0 + - pycparser=2.21=pyhd3eb1b0_0 + - pygments=2.15.1=py310h06a4308_1 + - pygraphviz=1.9=py310h5eee18b_1 + - pyinotify=0.9.6=py310h06a4308_0 + - pyopenssl=23.0.0=py310h06a4308_0 + - pyparsing=3.0.9=py310h06a4308_0 - pyqt=5.15.7=py310hab646b1_3 - pyqt5-sip=12.11.0=py310heca2aa9_3 - - pysocks=1.7.1=pyha2e5f31_6 - - pytest=7.3.1=pyhd8ed1ab_0 + - pysocks=1.7.1=py310h06a4308_0 - python=3.10.11=he550d4f_0_cpython - - python-dateutil=2.8.2=pyhd8ed1ab_0 - - python-tzdata=2023.3=pyhd8ed1ab_0 + - python-dateutil=2.8.2=pyhd3eb1b0_0 + - python-tzdata=2021.1=pyhd3eb1b0_0 - python_abi=3.10=3_cp310 - pythonocc-core=7.7.0=py310he34791d_1 - - pytz=2023.3=pyhd8ed1ab_0 + - pytz=2022.7=py310h06a4308_0 - pyvista=0.39.0=pyhd8ed1ab_0 - qt-main=5.15.8=h5d23da1_6 - - rapidjson=1.1.0=he1b5a44_1002 - - readline=8.2=h8228510_1 - - requests=2.29.0=pyhd8ed1ab_0 - - rich=13.3.5=pyhd8ed1ab_0 + - rapidjson=1.1.0=h6bb024c_0 + - readline=8.2=h5eee18b_0 + - requests=2.29.0=py310h06a4308_0 + - rich=13.3.5=py310h06a4308_0 - scalapack=2.2.0=hd931219_1 - - scipy=1.10.1=py310ha4c1d20_3 + - scipy=1.10.1=py310heeff2f4_1 - scooby=0.7.2=pyhd8ed1ab_0 - scotch=6.0.9=hb2e6521_2 - - setuptools=67.7.2=pyhd8ed1ab_0 - sip=6.7.9=py310hc6cd4ac_0 - - six=1.16.0=pyh6c4a22f_0 + - six=1.16.0=pyhd3eb1b0_1 - slepc=3.17.2=real_hb19a63e_100 - slepc4py=3.17.2=real_h45d0737_101 - - snappy=1.1.10=h9fff704_0 - - sqlite=3.42.0=h2c6b66d_0 - - stack_data=0.6.2=pyhd8ed1ab_0 - - suitesparse=5.10.1=h9e50725_1 + - snappy=1.1.9=h295c915_0 + - sqlite=3.41.2=h5eee18b_0 + - stack_data=0.2.0=pyhd3eb1b0_0 + - suitesparse=5.10.1=he2db622_2 - superlu=5.2.2=h00795ac_0 - superlu_dist=7.2.0=h25dcc4a_0 - svgwrite=1.4.3=pyhd8ed1ab_0 - svt-av1=1.4.1=hcb278e6_0 - - sysroot_linux-64=2.12=he073ed8_15 - - tbb=2021.9.0=hf52228f_0 - - tbb-devel=2021.9.0=hf52228f_0 + - sysroot_linux-64=2.17=h57e8cba_10 + - tbb-devel=2021.8.0=hdb19cb5_0 - tenacity=8.2.2=py310h06a4308_0 - - tk=8.6.12=h27826a3_0 - - toml=0.10.2=pyhd8ed1ab_0 - - tomli=2.0.1=pyhd8ed1ab_0 - - tornado=6.3.2=py310h2372a71_0 - - traitlets=5.9.0=pyhd8ed1ab_0 - - typing-extensions=4.5.0=hd8ed1ab_0 - - typing_extensions=4.5.0=pyha770c72_0 - - tzdata=2023c=h71feb2d_0 - - unicodedata2=15.0.0=py310h5764c6d_0 - - urllib3=1.26.15=pyhd8ed1ab_0 - - utfcpp=3.2.3=ha770c72_0 + - tk=8.6.12=h1ccaba5_0 + - toml=0.10.2=pyhd3eb1b0_0 + - tomli=2.0.1=py310h06a4308_0 + - tornado=6.2=py310h5eee18b_0 + - traitlets=5.7.1=py310h06a4308_0 + - typing-extensions=4.6.3=py310h06a4308_0 + - typing_extensions=4.6.3=py310h06a4308_0 + - tzdata=2023c=h04d1e81_0 + - unicodedata2=15.0.0=py310h5eee18b_0 + - urllib3=1.26.16=py310h06a4308_0 + - utfcpp=3.2.1=h06a4308_0 - vtk=9.2.5=qt_py310h8908f42_203 - - wcwidth=0.2.6=pyhd8ed1ab_0 - - wheel=0.40.0=pyhd8ed1ab_0 + - wcwidth=0.2.5=pyhd3eb1b0_0 + - wheel=0.38.4=py310h06a4308_0 - wslink=1.10.2=pyhd8ed1ab_0 - x264=1!164.3095=h166bdaf_2 - x265=3.5=h924138e_3 @@ -321,18 +331,17 @@ dependencies: - xorg-libxt=1.2.1=h7f98852_2 - xorg-renderproto=0.11.1=h7f98852_1002 - xorg-xextproto=7.3.0=h0b41bf4_1003 - - xorg-xproto=7.0.31=h7f98852_1007 - - xz=5.2.6=h166bdaf_0 - - yaml=0.2.5=h7f98852_2 - - yarl=1.9.1=py310h2372a71_0 - - zeromq=4.3.4=h9c3ff4c_1 - - zfp=0.5.5=h9c3ff4c_8 - - zipp=3.15.0=pyhd8ed1ab_0 + - xorg-xproto=7.0.31=h27cfd23_1007 + - xz=5.4.2=h5eee18b_0 + - yaml=0.2.5=h7b6447c_0 + - yarl=1.8.1=py310h5eee18b_0 + - zeromq=4.3.4=h2531618_0 + - zfp=0.5.5=h295c915_6 + - zipp=3.11.0=py310h06a4308_0 - zlib=1.2.13=h166bdaf_4 - - zstd=1.5.2=h3eb15da_6 - - pydantic - - pyyaml + - zstd=1.5.5=hc292b87_0 - pip: + - amworkflow==0.3 - anyio==3.7.0 - argon2-cffi==21.3.0 - argon2-cffi-bindings==21.2.0 @@ -357,7 +366,9 @@ dependencies: - jinja2==3.1.2 - jsonpointer==2.4 - jsonschema==4.17.3 + - jupyter==1.0.0 - jupyter-client==8.3.0 + - jupyter-console==6.6.3 - jupyter-core==5.3.1 - jupyter-events==0.6.3 - jupyter-server==2.7.0 @@ -373,6 +384,8 @@ dependencies: - nest-asyncio==1.5.6 - notebook==6.5.4 - notebook-shim==0.2.3 + - numpy-stl==3.0.1 + - occutils==0.1.dev0 - overrides==7.3.1 - pandocfilters==1.5.0 - plotly==5.15.0 @@ -381,22 +394,42 @@ dependencies: - psutil==5.9.5 - pydantic==1.10.9 - pyrsistent==0.19.3 + - pytest==7.4.0 - python-json-logger==2.0.7 + - python-utils==3.7.0 + - pyyaml==6.0 - pyzmq==25.1.0 + - qtconsole==5.4.3 + - qtpy==2.3.1 - rfc3339-validator==0.1.4 - rfc3986-validator==0.1.1 - ruamel-yaml==0.17.28 - ruamel-yaml-clib==0.2.7 - send2trash==1.8.2 + - setuptools==68.0.0 - sniffio==1.3.0 - soupsieve==2.4.1 - sqlalchemy==2.0.15 + - tbb==2020.3.254 - terminado==0.17.1 - tinycss2==1.2.1 + - trame==2.5.0 + - trame-client==2.9.4 + - trame-components==2.1.1 + - trame-deckgl==2.0.2 + - trame-markdown==2.0.2 + - trame-matplotlib==2.0.2 + - trame-plotly==2.1.1 + - trame-rca==0.3.1 + - trame-router==2.0.2 + - trame-server==2.11.4 + - trame-simput==2.3.2 + - trame-vega==2.0.3 + - trame-vtk==2.5.3 + - trame-vuetify==2.2.4 - uri-template==1.3.0 - webcolors==1.13 - webencodings==0.5.1 - websocket-client==1.6.1 - widgetsnbextension==3.6.4 - - polyfactory - - -e . + diff --git a/usecases/param_prism/map.stl b/usecases/param_prism/map.stl new file mode 100644 index 0000000..1ae71a7 Binary files /dev/null and b/usecases/param_prism/map.stl differ diff --git a/usecases/param_prism/terrain.stl b/usecases/param_prism/terrain.stl new file mode 100644 index 0000000..a30e85c Binary files /dev/null and b/usecases/param_prism/terrain.stl differ diff --git a/usecases/param_prism/tutorial1.py b/usecases/param_prism/tutorial1.py new file mode 100644 index 0000000..3919b74 --- /dev/null +++ b/usecases/param_prism/tutorial1.py @@ -0,0 +1,42 @@ +from amworkflow.src.utils.reader import read_stl_file +from amworkflow.src.geometries.property import get_occ_bounding_box, get_faces, get_face_center_of_mass +from OCC.Core.BRepClass import BRepClass_FaceExplorer +from amworkflow.src.geometries.builder import geometry_builder,sewer +from amworkflow.src.utils.writer import stl_writer +import os + + +# Read scanned STL file. +stl_path = os.getcwd() + "/map.stl" +print(stl_path) +scan = read_stl_file(filename=stl_path) +#The "scan" here is not yet a "TopoDS_Face" object in PyOCC, but a "TopoDS_Solid" object, which of course consist of faces. +faces = get_faces(scan) +# Using get_faces we can get a list of all faces from the solid object. +xmin, ymin, zmin, xmax, ymax, zmax = get_occ_bounding_box(scan) +# get the bounding box of this solid object. +# We will get rid of all the model basements in order to only work with the terrain surface. +bnd_x = [xmin,xmax] +bnd_y = [ymin,ymax] +bnd_z = [zmin] +bnd_face = [] +def check(num, list): + ''' + A small function for checking if the center point of each small face is either on the four basement walls. + ''' + for elem in list: + if abs(num - elem) < 1e-3: + return True + return False +for face in faces: + cnt = list(get_face_center_of_mass(face)) + #get center point of each face. + if check(cnt[0],bnd_x) or check(cnt[1], bnd_y) or check(cnt[2], bnd_z): + bnd_face.append(face) + +filter_face = [face for face in faces if face not in bnd_face] +#filter unwanted faces +sew_face = sewer(filter_face) +#Using sewer to sew all wanted faces into one face, the output is TopoDS_Face +stl_writer(sew_face, "terrain.stl") +#The result will be stored in the amworkflow/src/infrastructure/database/files/output_files. I have pasted the result in our working dir but you are welcome to check for sure. \ No newline at end of file diff --git a/usecases/param_prism/tutorial2.py b/usecases/param_prism/tutorial2.py new file mode 100644 index 0000000..1de3b98 --- /dev/null +++ b/usecases/param_prism/tutorial2.py @@ -0,0 +1,46 @@ +from amworkflow.src.geometries.property import get_occ_bounding_box, get_face_center_of_mass +from amworkflow.src.geometries.builder import geometry_builder +from amworkflow.src.geometries.operator import cutter3D +from amworkflow.src.geometries.simple_geometry import create_wire_by_points, create_prism, create_face +from OCC.Core.gp import gp_Pnt +from amworkflow.src.utils.writer import stl_writer +from amworkflow.src.utils.reader import stl_reader +from amworkflow.src.geometries.operator import reverse, scaler +from amworkflow.src.geometries.property import topo_explorer +#import stl file into OCC +imp = stl_reader("/home/yhe/Documents/amworkflow/usecases/param_prism/terrain.stl") +#get the bounding box of the import model +bbox = get_occ_bounding_box(imp) +#explore all edges in the model +edge = topo_explorer(imp, "edge") +xx = [] +yy = [] +#select all edges on the boundary. +for e in edge: + xmin, ymin, zmin, xmax, ymax, zmax = get_occ_bounding_box(e) + if (ymin + ymax < 1e-3) or (abs((ymin + ymax)*0.5 - bbox[4]) < 1e-3): + xx.append(e) + if (xmin + xmax < 1e-3) or (abs((xmin + xmax)*0.5 - bbox[3]) < 1e-3): + yy.append(e) +edges = xx + yy +#build a compound of all edges +wire = geometry_builder(edges) +#get the zmax of the new wire object for creating the basement. +wire_zmax = get_occ_bounding_box(wire)[-1] +prism = reverse(create_prism(wire,[0,0,-wire_zmax], True)) +#get the bounding box of the import model +xmin, ymin, zmin, xmax, ymax, zmax = get_occ_bounding_box(imp) +pts = [gp_Pnt(xmin, ymin, 0), + gp_Pnt(xmax, ymin, 0), + gp_Pnt(xmax, ymax, 0), + gp_Pnt(xmin, ymax, 0)] +#create the bottom of the basement +btm_wire = create_wire_by_points(pts) +btm_face = reverse(create_face(btm_wire)) +#create a cutter for trimming the prism. +cutter = create_prism(scaler(btm_face, get_face_center_of_mass(btm_face,True),1.2),[0,0,-15],True) +prism = reverse(cutter3D(prism, cutter)) +#sew the prism, the bottom face and the imported model together +output = geometry_builder([prism, btm_face, imp]) +#output the model. +stl_writer(output, "cutt.stl") diff --git a/usecases/param_wall/param_wall.py b/usecases/param_wall/param_wall.py index 3df8572..5178942 100644 --- a/usecases/param_wall/param_wall.py +++ b/usecases/param_wall/param_wall.py @@ -1,16 +1,11 @@ -import sys -import os -import pathlib -from OCC.Core.TopoDS import TopoDS_Shape from amworkflow.src.core.workflow import BaseWorkflow from amworkflow.src.geometries.simple_geometry import create_box -from amworkflow.src.interface.cli.cli_workflow import cli class ParamWallWorkflow(BaseWorkflow): - def __init__(self, args): - super().__init__(args) + def __init__(self): + super().__init__() - def geometry_spawn(self, param) -> TopoDS_Shape: + def geometry_spawn(self, param): pm = param box = create_box(length=pm.length, width= pm.width, @@ -19,8 +14,8 @@ def geometry_spawn(self, param) -> TopoDS_Shape: return box -p_wall = ParamWallWorkflow(cli()) +p_wall = ParamWallWorkflow() # print(cli()) -# p_wall.create() -# p_wall.mesh() -# p_wall.download() \ No newline at end of file +p_wall.create() +p_wall.mesh() +p_wall.download() \ No newline at end of file