19 Aug 2021

Solving the Ultimate Pit Limit Problem by Applying the Pseudoflow Algorithm

"Know how to solve every problem that has been solved." - R. Feynman (1988)



Programming Language: Python [Link App]


Content

1. Introduction
2. Problem statement
3. The solution
    3.1. Building an application + Features
4. Future work ideas
5. Code source


1. Introduction

Mining Engineers, and this is my kindly opinion, have learned about solving the Ultimate Pit Limit Problems by easy examples, i.e., Lerchs-Grossman 2D Algorithm. However, that is not what we see when running an open-pit mining operation. Software programs have solved most of the current problems, including this, and some mining engineers become users rather than doers. That being said, I decided to code the solution to the ultimate pit limit problem by applying the Pseudoflow algorithm (Hochbaum, 2008 [0]).


2. Problem statement

Given a 3D block model, how do we find the economic envelope/volume that contains the maximum value and fits within our operational constraints? i.e., maximum slope angles?

3D Block Model   Ultimate Pit Limit
3D Block Model   Ultimate Pit Limit

3. The solution

The solution follows a paper from Geovia Whittle, published in 2017. They explain how the Pseudoflow algorithm works in detail (Geovia, 2017 [1]). To make life easier, this is a summary on how the algorithm works:

1- Estimate each block’s value based on economic parameters:

Cutoff Grade = (MiningCost + ProcessingCost*(1 + Dilution))/(MetalPrice*Recovery)
for block in [1 ... Blocks]:
  block_value = (grade_of_block*Recovery*MetalPrice - (ProcessingCost+MiningCost))*Tons
  if grade_of_block < CutoffGrade:
    block_value = -MiningCost*Tons

2- Create a directed graph with our block model. For that:

  • Nodes: We will have 3 types of nodes:
    • Source: The graph starts here. Also, where the flow will start.
    • Sink: The graph ends here. Also, where the flow will end.
    • A block: Considered as a node
  • Edges:
    • Source -> a block: if the block's value is positive; the edge’s weight will be the block value.
    • Block -> block: if allowed by the precedence, i.e, 1-5 precedence (45 degrees); the edge’s weight will be the block value.
    • Block -> sink: if the block's value is negative; the edge’s weight will be the block value (negative).

Hint: You can use ‘Networkx’ to build your graph or do it by your own using dictionaries in Python.

def create_graph(self, bmodel, precedence):
    # Create an empty directed graph.
    Graph = NetX.DiGraph()
    if precedence == 9:
        distance = (self.modex**2 + self.modey**2+ self.modez**2)**0.5
    elif precedence == 5:
        distance = (self.modex**2 + self.modez**2)**0.5
    for step in range(steps_z):
        upper_bench = np.array(bmodel[bmodel.iloc[:,3]== col_compare[step]])
        lower_bench = np.array(bmodel[bmodel.iloc[:,3] == col_compare[step]])
        self.create_edges(
          Graph=Graph, up=upper, low =lower, trigger=step, prec=precedence, dist=distance)
        # Shrink the block model when going down - it reduces the computational time.
        bmodel = bmodel[
          (bmodel.iloc[:,1]!= self.minx+i*self.modex)
          &(bmodel.iloc[:,1]!=self.maxx-i*self.modex)
          &(bmodel.iloc[:,2] != self.miny+i*self.modey)
          &(bmodel.iloc[:,2] != self.maxy-i*self.modey)]
    return Graph

def create_edges(self,Graph,up, low, trigger, prec, dist):
    # Create internal edges - Block to block.
    tree_upper = spatial.cKDTree(up[:,1:4])
    # Get the closest block for each block, yet complying the precedences.
    mask = tree_upper.query_ball_point(low[:,1:4], r = dist + 0.01)
    for _, g in enumerate(mask):
        if len(g) == prec:
            for reach in up[g][:,0]:
              # Add internal edge + adding a weight of 99e9 (infinite).
                Graph.add_edge(low[_][0], reach, const = 99e9, mult = 1)
    #Create external edges - Source to block to sink.
    player = up
    for node, capacity in zip(player[:,0], player[:,4]):
        cap_abs = np.absolute(np.around(capacity, decimals=2))
        # Create an edge from a node to the sink if bvalue less than 0
        if capacity < 0:
            Graph.add_edge(node, self.sink, const = cap_abs, mult = -1)
        # Otherwise the source is connected to the block.
        else:
            Graph.add_edge(0, node, const = cap_abs, mult = 1)

3- The algorithm will push the flow from the source to an ore node and it will try to saturate the capacity. Furthermore, it will push from the waste node to pay for waste blocks. Therefore, as the maximum flow is found, the problem is solved and that will mean that waste blocks were paid. The following chart extracted from ‘Whittle’s paper’ would help you better understand what is written above:

Flow going through the graph
Flow going through the graph

3.1. Building an application + Features

To solve this problem dynamically, and also to make people playing with it, a web application has been created by using Streamlit.

Features of the Application:

  • You can insert your own block model or play with a default one.
Uploading a block model file
Uploading a block model file
  • It can check outliers after you upload a block model in .csv format. If you do not have it, use a default one. We have everything for you!

    • What do we mean with outliers?
      • Block’s coordinates that are out of the big block, meaning that an specific block does not belong to a 3D cubic beatiful form.
      • Block’s coordinates that are not in the proper gravity center.
  • 3D Visualization: Select some lower and upper bounds for what you want to see and grades’ ranges and we will color-code based on it.

3D Block Model Visualization
3D Block Model Visualization


  • GradeTonnage Distribution
Grade-Tonnage Distribution
Grade-Tonnage Distribution


  • Input the economic parameters to get the blocks’ value
User Inputs - Economic Parameters
User Inputs - Economic Parameters


  • Run the Algorithm and visualize your beatiful pit limit
User Inputs - Economic Parameters
User Inputs - Economic Parameters

4. Future work ideas

  • Add a variety of slope angles. At this point in time, we are just evaluating 2 precedences which are equivalent to 45 and 40 degrees approximately.
  • Evaluate a set of revenue factors and see where to mine first - which nested pit has the highest value. Also, visualize it.
  • Use some Operations Research techniques to draw automated inpit and expit ramps. Dr. Yarmuch developed an algorithm to solve this problem and his paper is worth reading.

5. Code source


Tags:
0 comments