31 Replies Latest reply on Apr 16, 2018 10:41 AM by Jacob Corder

    Mass-searching macro challenge

    J. R.

      Hello,

       

      As a part of a large simulation suite that I'm writing, I've come across an apparently simple, yet quite complicated issue.

       

      There is a solid body in Solidworks model, and a plane offset from Top Plane that intersects that model. The model is then cut in half with Surface Cut with that plane. The offset of the plane has to be adjusted so that the resulting mass of the body is within desired values. In other words, the macro has to solve the model for desired mass value, in as few rebuilds as possible. Here's a screenshot to help visualize it:

       

      mass search.jpg

       

      If it were any primitive shape (box, sphere, etc.) the solution could be found parametrically, but that won't work with complex or unknown shapes. Therefore, the macro has to compare the desired mass input by the user to the actual mass resulting after the cut is made at a certain height, and work it's magic until the numbers are close enough.

       

      So, to sum it up:

       

      Goal: adjust the height of the cut plane so that the mass of the body after the cut is within 1% of the target mass in as few rebuild cycles as possible.

       

      Setup:

      1. The model can be of any shape or size;

      2. The bottom of the model is always at zero in Y axis, and it's upper limit is known (provided by user or another function that is outside the scope this challenge);

      3. The maximum possible mass (prior to cut) is also known (provided by user or another function);

       

      Requirements:

      1. The cut plane height must never go out of bounds (below 0 or above the upper limit);

      2. The macro has to reach the desired mass value from any possible current height of the cut plane.

      2. The macro cannot do any pre-analyses of the model that involves any additional rebuilds, roll-backs, or extra sketches/features to the model.

       

      Here is the prepared code that only requires a search algorithm:

       

      Option Explicit
      
      Dim swApp As Object
      Dim swModel As SldWorks.ModelDoc2
      Dim swDimension As SldWorks.Dimension
      
      Sub main()
          Const HeightLimit As Double = 1
          Const IterationLimit As Double = 30
          Const Margin As Double = 0.01
          Dim Mass, TargetMass, MaxMass As Double
          Dim newHeight As Double
          Dim IterationCount As Double
          
          Set swApp = Application.SldWorks
          Set swModel = swApp.ActiveDoc
          
          MaxMass = 135 'for this specific model
          TargetMass = 50 'can be anything between 0 and MaxMass
          IterationCount = 0
          
          While Abs(TargetMass - Mass) > TargetMass * Margin And IterationCount < IterationLimit
              Mass = GetMass
              
              
              '***algorithm goes here***
              
      
              'set new height, rebuild, advance iteration count
              SetCutPlaneHeight (newHeight)
              swModel.EditRebuild3
              IterationCount = IterationCount + 1
              
              'messages
              Debug.Print "Iteration count = " & IterationCount & ", Mass = " & Mass
              If IterationCount = IterationLimit Then Debug.Print "Iteration limit Reached"
              If Abs(TargetMass - Mass) < TargetMass * Margin Then Debug.Print "Solution found"
          Wend
      End Sub
      
      Function GetCutPlaneHeight()
          Set swDimension = swModel.Parameter("D1@Cut plane")
          GetCutPlaneHeight = swDimension.SystemValue
      End Function
      
      Sub SetCutPlaneHeight(Height As Double)
          Set swDimension = swModel.Parameter("D1@Cut plane")
          swDimension.SystemValue = Height
      End Sub
      
      Function GetMass() As Double
          Dim nStatus As Long
          Dim vMassProp As Variant
          Dim Mass As Double
      
          vMassProp = swModel.Extension.GetMassProperties2(1, nStatus, True)
          GetMass = vMassProp(5)
      End Function
      

       

      As you can see, it involves simple procedures to get and set the height of the cut plane, and measure the mass of the model after the rebuild. The while cycle has an iteration count that ensures the cycle won't get stuck in an infinite loop. The debug.print system informs of each iteration results, as well as if the solution was found or the iteration count was exceeded.

       

      Now, I've been working on this problem for a few weeks now, and tried several approaches. The best approach that worked for me was:

      1. Measuring the difference between target mass and actual mass, and dividing it by the maximum mass;

      2. Using this value to set the increment (step size) of the cut plane height;

      3. Setting the correct direction to move the increment (+ or -);

      4. Advancing the cut plane height by the increment;

      5. If the actual mass value "jumps over" the target, the increment direction is reversed, and it's value is divided by 2.

      6. The cycle repeats until the actually mass reaches the limit.

      7. (There are some additional functions that "buffs" the increment if they see that applying it will make the cut plane height to go out of bounds)

       

      It works sort of okay, always finds a solution but since the algorithm is "blind", so to speak, it takes 7-12 rebuilds on average to find a solution, and sometimes more for very complex and unpredictable shapes.

       

      Another approach that I worked on, but never made work, was this:

      1. Measuring the difference between target mass and actual mass, and dividing it by the maximum mass;

      2. Using this value to set the increment (step size) of the cut plane height at every iteration.

       

      This algorithm is less "blind", because the increment size is driven by the size of the mass difference, but often it gets stuck because this can make the increment to be too large, and it skips the Goldilocks zone, or it makes the increment too small, and it takes forever to reach the target. This algorithm is great for simple, predictable (gradually changing) models, but is a catastrophe with complex ones.

       

      I also tried combining both methods, but it never worked out well either.

       

      So, I invite you to try your hand with this challenge (attaching the model and the macro below), and see if you can come up with an algorithm that will find an accurate solution within 3-4 rebuilds, even with very complex and unpredictable models. Try different target masses, try different models (just don't forget to update the constants in the macro), and see if your algorithm can find the solution from any initial position of the cut plane.

       

      Looking forward to it! Hopefully, this can be solved without writing a full AI suite

        • Re: Mass-searching macro challenge
          Rob Edwards

          Hi JR

          I would guess that for any shape and mass, your fastest result would be from just repeatedly cutting in half.

          So starting at 100%

          Cut @ 50% : If mass < result cut at 25% else cut @75%

          repeat

           

          considering the case when desired mass is always lower

           

          Rebuild 1      50%

          Rebuild 2      25%

          Rebuild 3      12.5%

          Rebuild 4      6.25%

          Rebuild 5      3.125%

          Rebuild 6      1.5625%

          Rebuild 7      0.78125%

           

          So it would take 7 rebuilds to get within 1% of your cutting plane range.

          How this relates to the mass I am not too sure - my Math and Computer Science Theory is a bit thin.

            • Re: Mass-searching macro challenge
              J. R.

              Well, yes, this approach kind of works, but it negates the possibility of finding close results quicker... For example, the current plane cut height is at 50%, and the mass is, say, 75 kg. I then set the desired mass to 80 kg. The result could now be found much quicker if that was taken into account, but your method would just start all over again... And 7 rebuilds is still quite a lot, I'm hoping for something more like 3-4 rebuilds.

            • Re: Mass-searching macro challenge
              Deepak Gupta

              Have you looked into using sensors?

                • Re: Mass-searching macro challenge
                  J. R.

                  Not really. How would that help in this case?

                    • Re: Mass-searching macro challenge
                      Artem Taturevych

                      Hi,

                       

                      Just couple of ideas.

                       

                      You can try to employ the center of mass and moments of inertia to get the idea of mass distribution in the body. So when iterating not just cut in half but based on the distance of COG to the center of the body.

                       

                      Another thing. This will not help you with the algorithm but help you with performance and general user experience. Make a temp copy of your body via Body2::Copy and work with this geometry by cutting it via Body2::Operations2. So it won't raise any rebuilds and all the operation may be completely invisible to users. Than at the end after the correct position is found - do one cut with one rebuild on the model.

                       

                      Thanks,
                      Artem

                        • Re: Mass-searching macro challenge
                          J. R.

                          Very interesting ideas, thank you. Could you please elaborate on that second part, making a temp copy of the body? How exactly would I go about performing all the required operations with that temp copy? I'm reading the documentation now on the Operations2 method, and I don't see how to do it. Forgive my inexperience, I just got started with programming a few months ago.

                            • Re: Mass-searching macro challenge
                              Artem Taturevych

                              Take a look at this example. It cuts the selected body in half and temp displays first half without modifications in feature tree

                               

                              Dim swApp As SldWorks.SldWorks

                              Dim swModel As SldWorks.ModelDoc2

                              Dim swSelMgr As SldWorks.SelectionMgr

                              Dim swModeler As SldWorks.Modeler

                               

                               

                               

                              Sub main()

                               

                               

                               

                                  Set swApp = Application.SldWorks

                                     

                                  Set swModeler = swApp.GetModeler

                                     

                                  Set swModel = swApp.ActiveDoc

                                 

                                  Set swSelMgr = swModel.SelectionManager

                                 

                                  Dim swBody As SldWorks.Body2

                                 

                                  Set swBody = swSelMgr.GetSelectedObject6(1, -1)

                                 

                                  swBody.HideBody True

                                 

                                  Dim swTempBody As SldWorks.Body2

                                 

                                  Set swTempBody = swBody.Copy

                                 

                                  Dim vBox As Variant

                                  vBox = swTempBody.GetBodyBox()

                                 

                                  Dim width As Double

                                  Dim length As Double

                                  Dim height As Double

                                 

                                  width = vBox(3) - vBox(0)

                                  height = vBox(4) - vBox(1)

                                  length = vBox(5) - vBox(2)

                                 

                                  Dim dBoxData(8) As Double

                                  dBoxData(0) = (vBox(3) + vBox(0)) / 2: dBoxData(1) = (vBox(4) + vBox(1)) / 2: dBoxData(2) = (vBox(5) + vBox(2)) / 2

                                  dBoxData(3) = 0: dBoxData(4) = 1: dBoxData(5) = 0

                                  dBoxData(6) = width: dBoxData(7) = length: dBoxData(8) = height

                                 

                                  Dim swCutBody As SldWorks.Body2

                                  Set swCutBody = swModeler.CreateBodyFromBox3(dBoxData)

                                     

                                  Set swTempBody = swTempBody.Operations2(swBodyOperationType_e.SWBODYCUT, swCutBody, 0)(0)

                                  swTempBody.Display3 swModel, 0, 0

                                 

                                  Stop

                                 

                                  swBody.HideBody False

                                 

                              End Sub

                                • Re: Mass-searching macro challenge
                                  J. R.

                                  I see. That looks like really advanced stuff, but I think I understand some of it. Thank you for taking time to write this code. I see you created a temporary body (swCutBody) to perform the cut on the main body. I assume there is no way to make the cut with a plane, since the documentation specifies that two bodies must be combined for the cut operation.

                                   

                                  A question, if I may. Can the swTempBody be moved or rotated prior to the cut operation? In my simulation suite, both the cut plane and the object are constantly re-positioned. It is easy enough with standard features, but I'm not sure if that can be done outside feature tree with this temporary body.

                                    • Re: Mass-searching macro challenge
                                      Artem Taturevych

                                      You can do both: you can cut by sheet (find the Cut Body in Half using Macro Feature Example (VBA) example in API help). You can reposition the body via Body2::ApplyTransform. These geometry APIs are derived directly from the Parasolid Kernel. You may assume that you can do everything with temp bodies that you can do with any standard feature of SOLIDWORKS as all of them are based on Parasolid geometry APIs.

                                        • Re: Mass-searching macro challenge
                                          J. R.

                                          That's fantastic. It appears that doing these operations with temporary body is MUCH faster than doing it through traditional rebuilds. This will cut down the calculation times so much that I probably won't need an extremely efficient algorithm that I came for in this topic. Thank you very much, Artem, your advice was truly indispensable.

                            • Re: Mass-searching macro challenge
                              J. R.

                              Artem Taturevych suggestion to use temporary bodies instead of SolidWorks' features helped a lot with performance, but a quicker mass search algorithm is still needed. Rob Edwards "half of half" suggestion was pretty efficient, but it doesn't take into account the current height of the cut plane. In some cases, there might be a need to run additional optimization functions while running the main one (for example, tilting the body to make sure it's new center of mass lines up with a certain vertical axis), and these rotations would "throw off" the mass of the cut body, and render "half of half" approach ineffective. Ideally, the algorithm should start the search from the current cut plane height, not 0 or half of the cut range.

                               

                              I would really appreciate it if anyone could give any suggestions on how to solve this.

                                • Re: Mass-searching macro challenge
                                  Simon Turner

                                  You could try using the Optimization functionality of Excel.

                                  I have done this before, but a while ago so I can't remember the exact details.

                                  But you can have 2 cells:

                                  A1 is the dimension which represents the height of the plane

                                  A2 is the calculated value of the mass

                                  You can tell Excel to vary the value of A1. Your macro will be run when the value of A1 changes and it will populate A2.

                                  Excel will then use some sort of Newton-Raphson iteration to quickly converge on the result you want.

                                  • Re: Mass-searching macro challenge
                                    John Alexander

                                    Have you considered using the C.G. to inform the step size? I'm not sure if temporary bodies would make that available. If you know the C.G. of the temporary body, you know where you would have to cut to go exactly halfway in the mass function.

                                     

                                    Edit: I see now that was already mentioned.

                                      • Re: Mass-searching macro challenge
                                        John Alexander

                                        If you formalize the problem in this way, it might be easier to think about.

                                         

                                        L: total length of part

                                        x: distance as a percentage of total length (distance between cut plane and origin). i.e. x = d/L if d is the actual distance.

                                        m(x): mass as a function of x

                                        m0: desired mass

                                        x0: distance at desired mass: m(x0) = m0

                                         

                                        The error in mass for given position intersects zero at e(x0) = m(x0) - m0.

                                        e(x) = m(x) - m0

                                         

                                        One observation is that m(x) is monotonic, strictly increasing. That is, for increasing values of x, m(x) is guaranteed to increase. Also, according to my interpretation of your problem, m(x) is not guaranteed to be continuous.

                                         

                                        Because e(x) crosses 0 at exactly one place, a root finding method seems like the way to go. There are several root-finding methods that don't require computing derivatives or having an analytic expression of the function:

                                        Bisection method - Wikipedia

                                        Secant method - Wikipedia

                                         

                                        The problem with these methods is they may not perform well if m(x) is not continuous. If your shape is like a dumbell for example, there will be discontinuities in the mass function m(x). When you move from the large diameter to the small diameter, the function will "jump" to a lower value. Any method that relies on approximating derivatives will probably encounter weirdness if the target value is close to that jump.

                                          • Re: Mass-searching macro challenge
                                            J. R.

                                            I am not too good at mathematics, and I'm having a hard time fully understanding what you suggested, but if I'm correct, this is some kind of interpolation. As it happens, just this morning, trying out different things, I wrote a linear interpolation function, and it worked wonders. It's different from what you suggested, but I suspect it works on the same principle. Basically it makes a random adjustment, measures the change in mass, and calculates a simple factor using the adjustment change and mass change to determine the size of the next step. It is simple, elegant, just 12 lines of code, and works extremely fast, 2-4 iterations on average to get to withing 1% of the target mass. With predictable shapes (consistent profile shape), it only takes 2 iterations, with highly irregular ones it can sometimes take as much as 6, but that's very rare. I am very pleased with that performance, and it's more than enough for my needs. But I will analyze your suggestion and the links you provided further. Thank you very much.

                                              • Re: Mass-searching macro challenge
                                                Jacob Corder

                                                You can spend months on something like this if you want to use mathematics. But in the end it's going to be solved through testing . The code I supplied makes life easy. And it is fast.  Save yourself the pain.

                                                 

                                                It's like the traveling salesman problem. It can be solved with brute Force meaning testing. Vs formulas that are much too complex for someone to maintain. It's not a good thing to be the only one who knows how code works.

                                                • Re: Mass-searching macro challenge
                                                  John Alexander

                                                  I didn't have a concrete suggestion, I was just brainstorming ways of representing the problem and existing methods that are used to solve them - no sense reinventing the wheel.

                                                   

                                                  The fastest-converging methods will take advantage of analytic expressions and derivatives (explicit formulas that describe the curve). In your case, that isn't possible, you have to measure the mass after slicing the model. The second best thing to do is to compute the derivative numerically by finite differences (which is similar to what you are doing from the sounds of it). This just means that you estimate the slope of the mass function by computing the mass at two different positions and fit a line between them.

                                                   

                                                  My last comment regarding discontinuity was incorrect. m(x) will be continuous.

                                          • Re: Mass-searching macro challenge
                                            Jacob Corder

                                            since temporary bodies are a pain to work with.. here is the solution you are looking for.

                                             

                                            read it and understand it. 

                                             

                                            to see the body that is cutting, insert a breakpoint after Display3

                                             

                                            it requires one rebuild,  possibly 2 if the dimension is not set at 1 meter. 

                                             

                                            there is a lot you need to do to customize this so enjoy

                                            • Re: Mass-searching macro challenge
                                              Jacob Corder

                                              also you seriously need to stop writing in VBA especially if your attempting to create a simulation package.

                                               

                                              migrate to .net or C++.  get away from VBA junk