Mass-searching macro challenge

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:

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

SolidworksApi macros