Note
Click here to download the full example code
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
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'}}
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)