Mass Balancing

In the examples so far, Flowsheet objects are created by math operations, so they inherently balance. A common problem in Mineral Processing (Metallurgy/Chemical/Process Engineering) is mass (or metallurgical) balancing. When auditing a processing plant the data is collected by measurement and sampling/assaying. This data will never balance of course due to sampling and measurement errors.

There exists a fundamental optimisation process that can balance the overall mass and components across a system (network/flowsheet). This example demonstrates that functionality.

import logging
from functools import partial
from typing import Dict

import numpy as np
import pandas as pd
import plotly

from elphick.mass_composition import MassComposition
from elphick.mass_composition.balance import MCBalance
from elphick.mass_composition.flowsheet import Flowsheet
from elphick.mass_composition.utils.partition import napier_munn
from elphick.mass_composition.datasets.sample_data import size_by_assay
logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s %(levelname)s %(module)s - %(funcName)s: %(message)s',
                    datefmt='%Y-%m-%dT%H:%M:%S%z')

Create a MassComposition object

We get some demo data in the form of a pandas DataFrame

df_data: pd.DataFrame = size_by_assay()
df_data
mass_dry fe sio2 al2o3
size_retained size_passing
0.850 2.000 3.3 64.15 2.04 2.68
0.500 0.850 9.9 64.33 2.05 2.23
0.150 0.500 26.5 64.52 1.84 2.19
0.075 0.150 2.5 62.65 2.88 3.32
0.045 0.075 8.8 62.81 2.12 2.25
0.000 0.045 49.0 55.95 6.39 6.34


Create the object

mc_size: MassComposition = MassComposition(df_data, name='size sample')
print(mc_size)
mc_size.aggregate()
size sample
<xarray.Dataset> Size: 336B
Dimensions:   (size: 6)
Coordinates:
  * size      (size) object 48B [0.85, 2.0) [0.5, 0.85) ... [0.0, 0.045)
Data variables:
    mass_wet  (size) float64 48B 3.3 9.9 26.5 2.5 8.8 49.0
    mass_dry  (size) float64 48B 3.3 9.9 26.5 2.5 8.8 49.0
    H2O       (size) float64 48B 0.0 0.0 0.0 0.0 0.0 0.0
    Fe        (size) float64 48B 64.15 64.33 64.52 62.65 62.81 55.95
    SiO2      (size) float64 48B 2.04 2.05 1.84 2.88 2.12 6.39
    Al2O3     (size) float64 48B 2.68 2.23 2.19 3.32 2.25 6.34
Attributes:
    mc_name:            size sample
    mc_vars_mass:       ['mass_wet', 'mass_dry']
    mc_vars_chem:       ['Fe', 'SiO2', 'Al2O3']
    mc_vars_attrs:      []
    mc_interval_edges:  {'size': {'left': 'retained', 'right': 'passing'}}
mass_wet mass_dry H2O Fe SiO2 Al2O3
name
size sample 100.0 100.0 0.0 60.09245 4.14753 4.27716


We partially initialise a partition function The dim argument is added to inform the split method which dimension to apply the function/split to

partition = partial(napier_munn, d50=0.150, ep=0.1, dim='size')

Create a Network that balances

Separate the object using the defined partition

mc_coarse, mc_fine = mc_size.split_by_partition(partition_definition=partition, name_1='coarse', name_2='fine')

fs: Flowsheet = Flowsheet().from_streams([mc_size, mc_coarse, mc_fine])
print(fs.balanced)

fig = fs.table_plot(plot_type='network', table_pos='left', table_area=0.3)
fig
True


Demonstrate that the data balances with the balance plot

fig = fs.plot_balance()
# noinspection PyTypeChecker
plotly.io.show(fig)  # this call to show will set the thumbnail for the gallery

The balance plot can be colored by a specified column or index/dimension.

fig = fs.plot_balance(color='size')
fig


Create an imbalanced network

Modify one stream to corrupt the balance

df_coarse_2 = mc_coarse.data.to_dataframe().apply(lambda x: np.random.normal(loc=x, scale=np.std(x)))
mc_coarse_2: MassComposition = MassComposition(data=df_coarse_2, name='coarse')
mc_coarse_2 = mc_coarse_2.set_parent_node(mc_size)

# create a new network - which does not balance
fs_ub: Flowsheet = Flowsheet().from_streams([mc_size, mc_coarse_2, mc_fine])
print(fs_ub.balanced)

fig = fs_ub.table_plot(plot_type='network', table_pos='left', table_area=0.3)
fig
False


fig = fs_ub.plot_balance()
fig


Balance the Flowsheet

Note

This example has not yet been completed…

mcb: MCBalance = MCBalance(fs=fs_ub)

# SD configuration
# df_sds: pd.DataFrame = mcb.create_balance_config(best_measurements='input')

# cost functions
cfs: Dict = mcb._create_cost_functions()
# check for a zero cost when passing the measured values
for k, v in cfs.items():
    x = mcb.fs.to_dataframe().loc[k, :].drop(columns=['mass_wet']).values.ravel()
    y = v(x=x)
    print(k, y)

df_bal: pd.DataFrame = mcb.optimise()

# create a network using the balanced data
fs_bal: Flowsheet = Flowsheet.from_dataframe(df=df_bal, name='balanced', mc_name_col='name')
fig = fs_bal.plot_parallel(color='name')
fig.show()
/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:72: RuntimeWarning:

invalid value encountered in divide

[0.85, 2.0) 44.18414624507857
/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:72: RuntimeWarning:

invalid value encountered in divide

[0.5, 0.85) 102.68716348473487
/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:72: RuntimeWarning:

invalid value encountered in divide

[0.15, 0.5) 6822.474633349426
/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:72: RuntimeWarning:

invalid value encountered in divide

[0.075, 0.15) 1299.7196730403964
/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:72: RuntimeWarning:

invalid value encountered in divide

[0.045, 0.075) 12.624316443587384
/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:72: RuntimeWarning:

invalid value encountered in divide

[0.0, 0.045) 44.12603423431622
/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:202: RuntimeWarning:

Method powell cannot handle constraints.

/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:202: OptimizeWarning:

Unknown solver options: xatol

/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:72: RuntimeWarning:

invalid value encountered in divide

Optimization terminated successfully.
         Current function value: 2.850809
         Iterations: 5
         Function evaluations: 1457
                Al2O3        Fe        H2O          SiO2      mass_dry  size
name
Node_in -7.002935e-05 -0.034756   0.000000  1.314848e-05 -5.095644e-02   NaN
coarse   1.096296e-02  3.196257 -14.036747 -8.560657e-04  4.524572e+00   NaN
fine     2.213041e-08  0.000011   0.000000  2.603801e-09  5.310747e-11   NaN
/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:202: RuntimeWarning:

Method powell cannot handle constraints.

/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:202: OptimizeWarning:

Unknown solver options: xatol

/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:72: RuntimeWarning:

invalid value encountered in divide

Optimization terminated successfully.
         Current function value: 6.922607
         Iterations: 11
         Function evaluations: 2729
            Al2O3        Fe        H2O      SiO2  mass_dry  size
name
Node_in  0.000078 -0.018427   0.000000 -0.000417  0.003000   NaN
coarse  -0.003995  1.305829 -32.769922  0.082481  7.984925   NaN
fine    -0.002750  0.683671   0.000000  0.014384 -3.448308   NaN
/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:202: RuntimeWarning:

Method powell cannot handle constraints.

/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:202: OptimizeWarning:

Unknown solver options: xatol

/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:72: RuntimeWarning:

invalid value encountered in divide

Optimization terminated successfully.
         Current function value: 1.994857
         Iterations: 52
         Function evaluations: 10763
            Al2O3        Fe         H2O      SiO2   mass_dry  size
name
Node_in  0.001444 -0.191930    0.000000  0.001535  -0.985332   NaN
coarse  -0.031624 -1.757421  -24.001292 -0.010482  10.265487   NaN
fine    -0.073742  0.038438 -357.364351 -0.080776  -7.259364   NaN
/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:202: RuntimeWarning:

Method powell cannot handle constraints.

/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:202: OptimizeWarning:

Unknown solver options: xatol

/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:72: RuntimeWarning:

invalid value encountered in divide

Optimization terminated successfully.
         Current function value: 21.834363
         Iterations: 6
         Function evaluations: 1501
            Al2O3         Fe         H2O      SiO2  mass_dry  size
name
Node_in -0.000113  -0.178690    0.000000 -0.000048 -0.060996   NaN
coarse   0.014591  10.036741  277.107219  0.004394  6.440876   NaN
fine     0.004370   5.484283    0.000000  0.001844  0.562201   NaN
/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:202: RuntimeWarning:

Method powell cannot handle constraints.

/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:202: OptimizeWarning:

Unknown solver options: xatol

/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:72: RuntimeWarning:

invalid value encountered in divide

Optimization terminated successfully.
         Current function value: 0.410410
         Iterations: 6
         Function evaluations: 1497
            Al2O3        Fe        H2O      SiO2  mass_dry  size
name
Node_in  0.000040 -0.003939   0.000000 -0.000010  0.017871   NaN
coarse   0.000738 -0.044401  19.263268 -0.000028 -0.102159   NaN
fine    -0.004298 -0.122773   0.000000  0.001070 -2.807691   NaN
/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:202: RuntimeWarning:

Method powell cannot handle constraints.

/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:202: OptimizeWarning:

Unknown solver options: xatol

/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/balance.py:72: RuntimeWarning:

invalid value encountered in divide

Optimization terminated successfully.
         Current function value: 1.042026
         Iterations: 26
         Function evaluations: 4708
            Al2O3        Fe       H2O      SiO2  mass_dry  size
name
Node_in  0.003753 -0.012161  0.000000 -0.013599 -0.080565   NaN
coarse  -0.081375 -0.100791 -0.081055  0.599332  0.897935   NaN
fine    -0.219394  0.237840  9.380482  0.684675  3.839377   NaN

Preparing MassComposition data:   0%|          | 0/3 [00:00<?, ?it/s]
Preparing MassComposition data: 100%|##########| 3/3 [00:00<00:00, 552.20it/s]

Creating MassComposition objects:   0%|          | 0/3 [00:00<?, ?it/s]
Creating MassComposition objects: 100%|##########| 3/3 [00:00<00:00, 22.76it/s]
Creating MassComposition objects: 4it [00:00, 30.28it/s]

Total running time of the script: ( 0 minutes 4.521 seconds)

Gallery generated by Sphinx-Gallery