Note
Click here to download the full example code
Partition Models
Partition models, (a.k.a. partition curves) define the separation of a unit operation / process.
In the one dimensional case, the Partition Number (PN) is represents the probability that a particle will report to the defined reference stream.
Consider a desliming cyclone that aims to separate a slurry at 150 micron. The reference stream is defined as the Underflow (UF), since that is the “stream of value” in our simple example.
TODO
Add a reference to partition curves.
from functools import partial
import numpy as np
import pandas as pd
import plotly
import plotly.graph_objects as go
from scipy.interpolate import PchipInterpolator
from elphick.mass_composition import MassComposition
from elphick.mass_composition.datasets.sample_data import size_by_assay
from elphick.mass_composition.flowsheet import Flowsheet
from elphick.mass_composition.utils.partition import napier_munn
from elphick.mass_composition.utils.pd_utils import calculate_partition
# sphinx_gallery_thumbnail_number = -1
Create a mass-composition object
We get some demo data in the form of a pandas DataFrame
df_data: pd.DataFrame = size_by_assay()
mc_feed: MassComposition = MassComposition(df_data, name='size sample')
print(mc_feed)
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'}}
Define and Apply the Partition
We partially initialise the partition function The dim argument is added to inform the split method which dimension to apply the function/split to
part_cyclone = partial(napier_munn, d50=0.150, ep=0.1, dim='size')
Separate the object using the defined partitions. UF = Underflow, OF = Overflow
mc_uf, mc_of = mc_feed.split_by_partition(partition_definition=part_cyclone, name_1='underflow', name_2='overflow')
fs: Flowsheet = Flowsheet().from_streams([mc_feed, mc_uf, mc_of])
fig = fs.table_plot(table_pos='left',
sankey_color_var='Fe', sankey_edge_colormap='copper_r', sankey_vmin=50, sankey_vmax=70)
fig
We’ll now get the partition data from the objects
df_partition: pd.DataFrame = mc_feed.calculate_partition(ref=mc_uf)
df_partition
/home/runner/work/mass-composition/mass-composition/elphick/mass_composition/mass_composition.py:1386: FutureWarning:
The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
Create an interpolator from the data. As a Callable, the spline can be used to split a MassComposition object.
da = np.linspace(0.01, df_partition.index.right.max(), num=500)
spline_partition = PchipInterpolator(x=df_partition.sort_index()['da'], y=df_partition.sort_index()['PN'])
pn_extracted = spline_partition(da)
Plot the extracted data, and the spline on the input partition curve to visually validate.
pn_original = part_cyclone(da) / 100
fig = go.Figure(go.Scatter(x=da, y=pn_original, name='Input Partition', line=dict(width=5, color='DarkSlateGrey')))
fig.add_trace(go.Scatter(x=df_partition['da'], y=df_partition['PN'], name='Extracted Partition Data', mode='markers',
marker=dict(size=12, color='red', line=dict(width=2, color='DarkSlateGrey'))))
fig.add_trace(
go.Scatter(x=da, y=pn_extracted, name='Extracted Partition Curve', line=dict(width=2, color='red', dash='dash')))
fig.update_xaxes(type="log")
fig.update_layout(title='Partition Round Trip Check', xaxis_title='da', yaxis_title='PN', yaxis_range=[0, 1.05])
# noinspection PyTypeChecker
plotly.io.show(fig)
There are differences in the re-created partition at the coarser sizes. It would be interesting to investigate if up-sampling in advance of partition generation would reduce this difference. Alternatively, the napier_munn parameteric partition function could be fitted to reduce the difference.
Pandas Function
The same functionality is available in pandas
df_partition_2: pd.DataFrame = mc_feed.data.to_dataframe().pipe(calculate_partition, df_ref=mc_uf.data.to_dataframe(),
col_mass_dry='mass_dry')
df_partition_2
pd.testing.assert_frame_equal(df_partition, df_partition_2)
Total running time of the script: ( 0 minutes 0.496 seconds)