Best Way to Calculate Tank Volumes at Different Fluid Heights

Best Way to Calculate Tank Volumes at Different Fluid Heights

harvey3ELEA
Advocate Advocate
837 Views
15 Replies
Message 1 of 16

Best Way to Calculate Tank Volumes at Different Fluid Heights

harvey3ELEA
Advocate
Advocate

Currently, I'm creating planes from the bottom of my irregular fill shape up from the bottom at 3" intervals, and then using the split command at each plane to slice the upper section away, yielding the necessary iProperties "Physical" tab data to convert into gallons.

 

While it's not hard to do 20 individual splits, it seems like this is a perfect candidate for either an ilogic code (of which I'm quite poor at compiling), or some other method that can handle this.

 

All suggestions welcomed.

 

Harvey

0 Likes
Accepted solutions (1)
838 Views
15 Replies
Replies (15)
Message 2 of 16

a.brusamolino
Enthusiast
Enthusiast

Hi Harvey! 

Are you interested in calculating the volume or in automating the creation of the planes?
By "SPLIT," do you mean this command?

 

Divisione.png

 

Many thanks!

0 Likes
Message 3 of 16

harvey3ELEA
Advocate
Advocate

I'd be interested in something that creates the planes and then generates a summary sheet (Notepad would be fine) that compiles the volume in [in^3] and [US gallon] units per each remaining body under each active split.

 

My Split button is different from yours probably based on different versions of Inventor.

 

I'm running Inventor Professional 2023.5.3

 

Thanks.

0 Likes
Message 4 of 16

WCrihfield
Mentor
Mentor

This is an interesting topic.  Not one that I am that familiar with though, since I do not encounter irregularly shaped fluid containers, or the need for calculating volumes at specific levels within the containers where I work.  Something does come to mind though, if this is in the 'part' environment, rather than the 'assembly' environment.  The SurfaceBody object (in the Inventor API) can be 'solid' or just surfaces, but it has a property named Volume, which you may be able to use, from a code standpoint.  There is also the SurfaceBody.MassProperties property, which gives us a SurfaceBodyMassProperties object, which has lots of useful properties and methods.  We can also access that information manually, by expanding the 'Solid Bodies' folder in the model browser tree, right-clicking on a body, and choosing 'Properties' from the right-click menu.  The 'Body Properties' dialog that shows gives us similar functionality to the 'Physical' tab in the iProperties dialog, but specific to just that one body.  It shows us Mass, Area, Volume, and the X,Y,Z coordinates of its Center Of Gravity, with an Update button.

I was thinking that if the 'fluid' within the tank was a single solid body, in a multi-body part, with a WorkPlane defining the top of its fluid level, we could then get the volume of that fluid body.

If working in the assembly environment, then you may be dealing with ComponentOccurrence objects, instead of SurfaceBodies.  That object also has the ComponentOccurrence.MassProperties property which gives us access to a regular MassProperties object with even more properties & methods available to it.  Just some food for thought, and inspiration for future code solution development in that area.  We would need some sort of 'example' model file(s) to play around with though, if we were to develop something, because it would likely require some trial & error type testing along the way.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 5 of 16

a.brusamolino
Enthusiast
Enthusiast

One question: is the model you're using to calculate the fluid volume the fluid container itself, meaning the hollow solid, or is it already the "negative" shape that represents the fluid inside? Because, reading it more carefully, if you're getting the volume from the iProperties each time you apply the split, it sounds like you're actually working with the fluid volume, not the container.
As @WCrihfield  mentioned, if you could share the file, that would be even better

0 Likes
Message 6 of 16

harvey3ELEA
Advocate
Advocate

Wesley, this water core shape is indeed developed at part level (solid), which we shape our different tanks around.  We produce different shapes that follow a particular style, and they can be anywhere from 2,000 gallons all the way up to 15,000 gallons.

 

We're looking to incorporate a new Keyence FR radar level detection system and we need to know the upper volume (80%) to prevent an overfill condition, and also an "empty" volume (5-10%) that prevents our pumps from running dry.

 

I'd love to grab a code that can run at part level to give us the various volumes at 3" or 6" increments on a variety of tank sizes, but I'm not even remotely close to write iLogic codes.

 

I've been doing this manually, and while not difficult, it is extremely tedious.

 

Harvey

0 Likes
Message 7 of 16

harvey3ELEA
Advocate
Advocate

A.brusamolino, the shapes we generate as indiviual part files represent the fluid inside.  We shell/build our tanks around those specific-sized shapes.

0 Likes
Message 8 of 16

a.brusamolino
Enthusiast
Enthusiast

Ok, thanks!

So, this code reproduces the behavior you perform manually.
Basically, you need to provide the total height of the solid (it could be detected automatically by the code, but it's not straightforward), select the face that represents the top of your fluid column, and then it does the rest.
I wrote it a bit quickly, so check if the conversions are correct.
I just ran it on a simple case and it seems to work. If needed, I’ll take a closer look tomorrow and try to improve a few things.

 

' === INPUT UTENTE ===
Dim hMax_cm As Double = CDbl(InputBox("Altezza massima da riempire (in cm):", "Volume liquido", "30")) ' input a mano ma può essere fatto riconoscore automaticamente, un po' più complesso però

' Valore soglia per registrare i volumi ogni 3" (≈ 7.62 cm)
Dim soglia_cm As Double = 7.62

' === RIFERIMENTI BASE ===
Dim oDoc As PartDocument = ThisDoc.Document
Dim oDef As PartComponentDefinition = oDoc.ComponentDefinition
Dim oBody As SurfaceBody = oDef.SurfaceBodies.Item(1)

' === SELEZIONE FACCIA BASE ===
Dim baseFace As Face = ThisApplication.CommandManager.Pick(SelectionFilterEnum.kPartFaceFilter, "Seleziona la faccia di riferimento (base liquido)")
Dim basePlane As WorkPlane = oDef.WorkPlanes.AddByPlaneAndOffset(baseFace, 0)
basePlane.Visible = False

' === INIZIALIZZAZIONE ===
Dim hAttuale As Double = 0

' === FILE TXT ===
Dim folderPath As String = ThisDoc.Path
Dim filePath As String = folderPath & "\VolumiOgni3Pollici.txt"
Dim sw As System.IO.StreamWriter = System.IO.File.CreateText(filePath)
sw.WriteLine("Quota (cm) | Volume (cm³) | Volume (In³) | Galloni")

' === CICLO DI CALCOLO ===
While hAttuale <= hMax_cm
    ' Crea piano temporaneo
    Dim workPlane As WorkPlane = oDef.WorkPlanes.AddByPlaneAndOffset(basePlane, -hAttuale)
    workPlane.Visible = False
	
	Dim oNewSplit As SplitFeature = oDef.Features.SplitFeatures.TrimSolid(workPlane, oBody, False)
	Dim oNewBody As SurfaceBody = oNewSplit.SurfaceBodies.Item(1)
	Dim NewVolume As Double = oNewBody.Volume(0.01) '(cm³)
	
	Dim Inches As Double = NewVolume *2.54^3
	Dim gallons As Double = NewVolume * 0.000264172
	sw.WriteLine(Math.Round(hAttuale, 2) & " | " & Math.Round(NewVolume, 2) & " | " & Math.Round(Inches, 2) & " | " & Math.Round(gallons, 2))

    ' Passo successivo
    hAttuale += soglia_cm
End While

sw.Close()

' === MESSAGGIO FINALE ===
MessageBox.Show("Calcolo completato!" & vbCrLf & _
                "File salvato in:" & vbCrLf & filePath, "Risultato")

 

0 Likes
Message 10 of 16

harvey3ELEA
Advocate
Advocate

That works rather nicely, a.brusamolino!

 

I did try to convert the code text into English, but something changes that causes a number of errors.  Thought I could also tweak it a bit into non-metric, but was unsuccessful.

 

Nevertheless, the output text file from your original code is a valuable document that I can use as is.

 

Great work, and thanks!

 

Harvey

Message 11 of 16

a.brusamolino
Enthusiast
Enthusiast

Hi! I’ve cleaned up the code. Now even the variables are in English, so it’s easier to understand.
The height is calculated automatically based on the selected face (which now must be the bottom of the fluid volume, not the top anymore).
Once the calculation is done, the code deletes the temporary features so it doesn’t clutter the model tree if they’re not needed.

Dim oTG As TransientGeometry = ThisApplication.TransientGeometry

' === THRESHOLD SETUP (3 inches ≈ 7.62 cm) ===
Dim threshold_cm As Double = 7.62

' === BASE REFERENCES ===
Dim oDoc As PartDocument = ThisDoc.Document
Dim oDef As PartComponentDefinition = oDoc.ComponentDefinition
Dim oBody As SurfaceBody = oDef.SurfaceBodies.Item(1)
Dim oRange As Box = oBody.RangeBox

' === SELECT BASE FACE ===
Dim baseFace As Face = ThisApplication.CommandManager.Pick(SelectionFilterEnum.kPartFaceFilter, "Select the reference face (bottom of fluid)")
Dim basePlane As WorkPlane = oDef.WorkPlanes.AddByPlaneAndOffset(baseFace, 0)
basePlane.Visible = False

' === NORMAL VECTOR OF FACE ===
Dim faceEval As SurfaceEvaluator = baseFace.Evaluator
Dim origin As Point = baseFace.PointOnFace
Dim points() As Double = {origin.X, origin.Y, origin.Z}
Dim normal(2) As Double
faceEval.GetNormalAtPoint(points, normal)
Dim normalVec As Vector = oTG.CreateVector(normal(0), normal(1), normal(2))

' === CALCULATE MAX HEIGHT ALONG NORMAL ===
Dim hMax_cm As Double = 0
For Each corner As Point In {oRange.MinPoint, oRange.MaxPoint, _
                             oTG.CreatePoint(oRange.MinPoint.X, oRange.MinPoint.Y, oRange.MaxPoint.Z), _
                             oTG.CreatePoint(oRange.MinPoint.X, oRange.MaxPoint.Y, oRange.MinPoint.Z), _
                             oTG.CreatePoint(oRange.MaxPoint.X, oRange.MinPoint.Y, oRange.MinPoint.Z), _
                             oTG.CreatePoint(oRange.MaxPoint.X, oRange.MaxPoint.Y, oRange.MinPoint.Z), _
                             oTG.CreatePoint(oRange.MaxPoint.X, oRange.MinPoint.Y, oRange.MaxPoint.Z), _
                             oTG.CreatePoint(oRange.MinPoint.X, oRange.MaxPoint.Y, oRange.MaxPoint.Z)}
    
    Dim vec As Vector = corner.VectorTo(origin)
    Dim projLen As Double = vec.DotProduct(normalVec) ' cm
    If projLen > hMax_cm Then
        hMax_cm = projLen
    End If
Next

' === INITIALIZATION ===
Dim currentHeight As Double = 0

' === TXT FILE SETUP ===
Dim folderPath As String = ThisDoc.Path
Dim filePath As String = folderPath & "\VolumesEvery3Inches.txt"
Dim sw As System.IO.StreamWriter = System.IO.File.CreateText(filePath)
sw.WriteLine("Height (cm) | Volume (cm³) | Volume (in³) | Gallons")

' === LOOP ===
While currentHeight <= hMax_cm
	' Create a temporary work plane at the current height
    Dim workPlane As WorkPlane = oDef.WorkPlanes.AddByPlaneAndOffset(basePlane, -currentHeight)
    workPlane.Visible = False

    ' Split body
    Dim newSplit As SplitFeature = oDef.Features.SplitFeatures.TrimSolid(workplane, oBody, True)
    Dim newBody As SurfaceBody = newSplit.SurfaceBodies.Item(1)
    Dim volume_cm3 As Double = newBody.Volume(0.01)

    ' Convert
    Dim volume_in3 As Double = volume_cm3 * 0.0610237
    Dim volume_gallons As Double = volume_cm3 * 0.000264172

    ' Write
    sw.WriteLine(Math.Round(currentHeight, 2) & " | " & _
                 Math.Round(volume_cm3, 2) & " | " & _
                 Math.Round(volume_in3, 2) & " | " & _
                 Math.Round(volume_gallons, 2))

	newSplit.Delete
	workplane.Delete

    currentHeight += threshold_cm
End While

basePlane.Delete
sw.Close()

' === DONE ===
MessageBox.Show("Calculation completed!" & vbCrLf & _
                "File saved to:" & vbCrLf & filePath, "Done")
0 Likes
Message 12 of 16

harvey3ELEA
Advocate
Advocate

a.brusamolino, that runs even better than before.  Again, very nice work.

 

Two changes if you have the time (and so I don't screw up the code):

- can you modify it to do height in inches at the text file output stage?

- list gallons as the 2nd column in the output text file?

 

Thanks, Harvey

0 Likes
Message 13 of 16

a.brusamolino
Enthusiast
Enthusiast
Accepted solution

I'm glad it's working!
No worries at all — here's the updated code (you'll have to forgive me... I'm just not used to thinking in the imperial system ahah).

 

Dim oTG As TransientGeometry = ThisApplication.TransientGeometry

' === THRESHOLD SETUP (3 inches ≈ 7.62 cm) ===
Dim threshold_in As Double = 3
Dim in_to_cm_conversion As Double = 2.54

' === BASE REFERENCES ===
Dim oDoc As PartDocument = ThisDoc.Document
Dim oDef As PartComponentDefinition = oDoc.ComponentDefinition
Dim oBody As SurfaceBody = oDef.SurfaceBodies.Item(1)
Dim oRange As Box = oBody.RangeBox

' === SELECT BASE FACE ===
Dim baseFace As Face = ThisApplication.CommandManager.Pick(SelectionFilterEnum.kPartFaceFilter, "Select the reference face (bottom of fluid)")
Dim basePlane As WorkPlane = oDef.WorkPlanes.AddByPlaneAndOffset(baseFace, 0)
basePlane.Visible = False

' === NORMAL VECTOR OF FACE ===
Dim faceEval As SurfaceEvaluator = baseFace.Evaluator
Dim origin As Point = baseFace.PointOnFace
Dim points() As Double = {origin.X, origin.Y, origin.Z}
Dim normal(2) As Double
faceEval.GetNormalAtPoint(points, normal)
Dim normalVec As Vector = oTG.CreateVector(normal(0), normal(1), normal(2))

' === CALCULATE MAX HEIGHT ALONG NORMAL ===
Dim hMax_cm As Double = 0
For Each corner As Point In {oRange.MinPoint, oRange.MaxPoint, _
                             oTG.CreatePoint(oRange.MinPoint.X, oRange.MinPoint.Y, oRange.MaxPoint.Z), _
                             oTG.CreatePoint(oRange.MinPoint.X, oRange.MaxPoint.Y, oRange.MinPoint.Z), _
                             oTG.CreatePoint(oRange.MaxPoint.X, oRange.MinPoint.Y, oRange.MinPoint.Z), _
                             oTG.CreatePoint(oRange.MaxPoint.X, oRange.MaxPoint.Y, oRange.MinPoint.Z), _
                             oTG.CreatePoint(oRange.MaxPoint.X, oRange.MinPoint.Y, oRange.MaxPoint.Z), _
                             oTG.CreatePoint(oRange.MinPoint.X, oRange.MaxPoint.Y, oRange.MaxPoint.Z)}
    
    Dim vec As Vector = corner.VectorTo(origin)
    Dim projLen As Double = vec.DotProduct(normalVec) ' cm
    If projLen > hMax_cm Then
        hMax_cm = projLen
    End If
Next
Dim hMax_in As Double = hMax_cm / in_to_cm_conversion

' === INITIALIZATION ===
Dim currentHeight_in As Double = 0

' === TXT FILE SETUP ===
Dim folderPath As String = ThisDoc.Path
Dim filePath As String = folderPath & "\VolumesEvery3Inches.txt"
Dim sw As System.IO.StreamWriter = System.IO.File.CreateText(filePath)
sw.WriteLine("Height (in) | Volume (gal) | Volume (in³) | Volume (cm³)")

' === LOOP ===
While currentHeight_in <= hMax_in
	' Create a temporary work plane at the current height
    Dim workPlane As WorkPlane = oDef.WorkPlanes.AddByPlaneAndOffset(basePlane, -currentHeight_in * in_to_cm_conversion)
    workPlane.Visible = False

    ' Split body
    Dim newSplit As SplitFeature = oDef.Features.SplitFeatures.TrimSolid(workplane, oBody, True)
    Dim newBody As SurfaceBody = newSplit.SurfaceBodies.Item(1)
    Dim volume_cm3 As Double = newBody.Volume(0.01)

    ' Convert
    Dim volume_in3 As Double = volume_cm3 * 0.0610237
    Dim volume_gallons As Double = volume_cm3 * 0.000264172

    ' Write
    sw.WriteLine(Math.Round(currentHeight_in, 2) & " | " & _
				 Math.Round(volume_gallons, 2) & " | " & _
                 Math.Round(volume_in3, 2) & " | " & _
                 Math.Round(volume_cm3, 2))

	newSplit.Delete
	workplane.Delete

    currentHeight_in += threshold_in
End While

basePlane.Delete
sw.Close()

' === DONE ===
MessageBox.Show("Calculation completed!" & vbCrLf & _
                "File saved to:" & vbCrLf & filePath, "Done")

 
If it helped, feel free to mark it as the Accepted Solution.
Really appreciate it — many thanks!
Andrea

0 Likes
Message 14 of 16

harvey3ELEA
Advocate
Advocate

a.brusamolino, no problem at all on the metric vs. imperial concerns.  I wish the US would adopt metric as it's typically easier to work with, but we're stuck over here with our tape measures & rulers in feet & inches....

 

I'll give this code a run during my first break and let you know how it works.

 

Harvey

Message 15 of 16

harvey3ELEA
Advocate
Advocate

Works like a charm, Andrea!

Again, thank you very much for ironing this out for me.

Harvey

Message 16 of 16

a.brusamolino
Enthusiast
Enthusiast

No worries, Harvey!! Glad it works and was helpful.
Cheers!

Andrea