Optimizing Copper Extraction: Integrated Pyrometallurgical Modelling with ChemApp for Python – Part I

Introduction

Process modelling is a key approach for optimizing industrial operations, delivering tangible gains in cost reduction, process efficiency, and environmental performance. In copper extraction, pyrometallurgical routes consist of multiple interconnected unit operations that must be considered together to understand overall behavior. Using its stream functionality, ChemApp for Python provides rigorous thermochemical calculations that allow engineers to link these unit operations into an integrated copper process flowsheet. This post presents a step-by-step guide for metallurgists and engineers on building such Cu flowsheets and computing copper output and purity under defined operating conditions for each process step. During copper extraction, the two main performance targets are the total copper recovered and its purity in blister copper, which typically contains 96-98% copper. Blister copper’s quality determines its value and suitability for downstream refining and industrial uses. Its high copper content and versatility make it a crucial raw material across industries like electrical, construction, and manufacturing, driving economic value by serving as the primary feedstock for further processing.

In this blog post, we take the first step toward building an integrated process model for primary copper production in ChemApp for Python. With the release of ChemApp for Python version 8.3.4 [more info], new capabilities now allow us to model complex, multi-step metallurgical flowsheets within a single, integrated framework with significant advantages in terms of flexibility, accuracy, and computational efficiency.

The aim of this blogpost is to establish a solid foundation by developing an integrated process model that captures the primary production of copper flowsheet (mentioned below) from concentrate to metal. This process model will be utilized in the upcoming blog posts, where we will tackle real Cu metallurgical challenges.

The Blogpost on Github can be found https://gist.github.com/Shivani-Gonde/dd81aba6e2c06ec100ade15e20203ef6

Primary production of copper from CuFeS₂ (Chalcopyrite)

Setting up the Python script

The initial step requires loading the appropriate resources and relevant thermochemical database, as shown below:

# Importing ChemApp modules
from chemapp.core import AmountUnit, TemperatureUnit, PressureUnit, EnergyUnit
from chemapp.friendly import Units as cau
from chemapp.friendly import ThermochemicalSystem as cats
from chemapp.friendly import StreamCalculation as casc
# Load the thermodynamic database
cats.load("CuMet.cst")
# Set units
cau.set(A=AmountUnit.kg, T=TemperatureUnit.C, P=PressureUnit.bar, E=EnergyUnit.J)Code language: PHP (php)

Notes

  • The thermodynamic database CuMet.cst contains all data required to perform equilibrium calculations and must be loaded before any unit operation in the flowsheet can be evaluated.
  • .cst file can be created by selecting all the relevant phases using FactPS, FToxid, and FTmisc databases in Equilib module of FactSage. The CST version of this database is encrypted and proprietary to GTT Technologies.
  • The units for amount, temperature, pressure, and energy are defined at the start of the code to ensure consistent calculations throughout the model.

Unit operations in the Cu extraction process flowsheet

The figure represents the primary production of copper from CuFeS₂ (Chalcopyrite ore) process flowsheet visualized in FactFlow. FactFlow is a free FactSage 8.4 thermochemical process simulation add-on. It features an interactive flowsheeting GUI that streamlines complex equilibrium workflows, including stream management and recycling. This user-friendly interface provides an efficient alternative to traditional multiple equilibrium calculations while allowing users to fully utilize FactSage databases for their processes. For more information on FactFlow, please check: [FactSage Process Simulators], [FactFlow Manual]. In this series of blogposts, the Cu extraction process mentioned in [FactFlow] has been adapted for building the process model in ChemApp For Python.

Setting up the Python script

The unit operations in the simplified extraction process are:

  • Smelter
  • Converter (slag-forming)
  • Converter for Blister (copper-making)

Defining functions for individual unit operations

Smelter

The smelter has three input streams:

  • Copper input (while this example uses a relatively pure ore, the same process-modelling approach can easily be extended to feed materials containing impurities).
  • Gas input
  • SiO2 input

Streams serve as the inter-unit connections that support detailed modeling of material movements within complex industrial processes. Input streams can be defined using the built-in one-line function in ChemApp by specifying chemical constituents, constituent amounts (kg in this example), and the stream temperature and pressure:

Copper_input = casc.create_st_cfs("Copper_input", ["CuFeS2", "H2O"], [920, 80], T=25.0, P=1.0)
Gas_input = casc.create_st_cfs("Gas_input", ["O2", "N2"], [294.032, 385.968], T=25.0, P=2.6)
SiO2_input = casc.create_st_cfs("SiO2_input", ["SiO2"], [100], T=25.0, P=1.0)Code language: JavaScript (javascript)

The smelter produces three main streams of interest: slag, matte, and off-gas. A dedicated Smelter function is defined that takes these streams, along with the smelter temperature and pressure, as inputs and performs an isothermal equilibrium calculation under those conditions. The resulting smelter products can then be split into separate output streams using the built-in create_stream function in ChemApp for Python by specifying which phases to include in each stream, for example the gas phase for the off-gas stream and the matte phase for the matte stream.

In this case, isothermal calculations were performed. Temperature and pressure can be set to the experimental values, allowing isothermal (as shown here), adiabatic, and other types of T-P calculations to be carried out.

def Smelter(Copper_input, Gas_input, SiO2_input, Smelter_T, Smelter_P):
    Sm_streams = [Copper_input, Gas_input, SiO2_input]
    Smelter_result = casc.reaction_isothermal(Sm_streams, T=Smelter_T, P=Smelter_P)

    Slag_output_smelter = Smelter_result.create_stream("Slag_output", phs_include=["Slag-liq#1"])
    Matte_output_smelter = Smelter_result.create_stream("Matte_output", phs_include=["Matte"])
    Off_gas_smelter = Smelter_result.create_stream("Off_gas_smelter", phs_include=["gas_ideal"])
    return Slag_output_smelter, Matte_output_smelter, Off_gas_smelterCode language: JavaScript (javascript)

The input streams can then be passed into the Smelter function, which returns the streams of interest from the Smelter output. Example call:

Slag_output_smelter, Matte_output_smelter, Off_gas_smelter = Smelter(
    Copper_input, Gas_input, SiO2_input, 1250, 2.6
)

Similarly, dedicated functions (Converter & ConverterForBlister) are defined for the converter and converter for blister respectively in the flowsheet. The matte stream leaving the smelter serves as the feed stream to the converter, and the matte stream produced by the converter then becomes the input to the downstream converter for blister. In both converter steps, the functions take the relevant input streams together with the operating temperature and pressure as arguments and return the corresponding output streams of interest.

Converter

def Converter(Gas_input, SiO2_input, Matte_input, Converter_T, Converter_P):
    Converter_result = casc.reaction_isothermal(
        [Gas_input, SiO2_input, Matte_input], T=Converter_T, P=Converter_P
    )
    Matte_output_converter = Converter_result.create_stream("MatteForBlister", phs_include=["Matte"])
    SiO2_output_converter = Converter_result.create_stream(
        "SiO2_output", phs_include=["Slag-liq#1", "SiO2_Tridymite(h)(s4)"]
    )
    Gas_output_converter = Converter_result.create_stream("Gas_output", phs_include=["gas_ideal"])
    return Matte_output_converter, SiO2_output_converter, Gas_output_converterCode language: JavaScript (javascript)

Converter for blister

def ConverterForBlister(Gas_input_ConverterForBlister, Matte_input_stream, ConverterForBlister_T, ConverterForBlister_P):
    ConverterForBlister_result = casc.reaction_isothermal(
        [Gas_input_ConverterForBlister, Matte_input_stream], T=ConverterForBlister_T, P=ConverterForBlister_P
    )
    Copper_liq_speiss_output_ConverterForBlister = ConverterForBlister_result.create_stream(
        "Cu_From_ConverterForBlister", phs_include=["Cu-liq_or_speiss"]
    )
    Matte_output_ConverterForBlister = ConverterForBlister_result.create_stream(
        "Cu_From_ConverterForBlister", phs_include=["Matte"]
    )
    Spinel_output_ConverterForBlister = ConverterForBlister_result.create_stream(
        "Spinel_slag_From_ConverterForBlister", phs_include=["Spinel"]
    )
    Slag_output_ConverterForBlister = ConverterForBlister_result.create_stream(
        "Spinel_slag_From_ConverterForBlister", phs_include=["Slag-liq#1"]
    )
    Gas_output_ConverterForBlister = ConverterForBlister_result.create_stream("Gas_output", phs_include=["gas_ideal"])
    return (
        ConverterForBlister_result,
        Copper_liq_speiss_output_ConverterForBlister,
        Matte_output_ConverterForBlister,
        Spinel_output_ConverterForBlister,
        Slag_output_ConverterForBlister,
        Gas_output_ConverterForBlister
    )Code language: JavaScript (javascript)

A heat balance (ΔH) can be calculated for each furnace: the smelter, the converter, and the converter for blister.

Function for the integrated process model

An integrated process model is constructed by linking the output streams of each unit operation to the corresponding input streams of downstream units. The overall flowsheet function, CuMetallurgy_Flow, uses as input the copper, gas, and SiO2 streams to the smelter; the gas and SiO2 streams to the converter; and the gas stream to the converter for blister, together with the operating temperature and pressure for each of these units. Within the CuMetallurgy_Flow function, the previously defined unit-operation functions (SmelterConverterConverterForBlister) are called with their respective input streams and process conditions to compute the smelter, converter, and blister-converter output streams. Stream connections are established by feeding selected outputs into downstream units, for example, using the smelter matte stream as an input to the converter and the converter matte stream as an input to the converter for blister, as indicated in the process flow diagram. Finally, the copper amount and copper grade in the blister Cu stream (Cu-liq_or_speiss) leaving the converter for blister are obtained by analyzing the phase composition of this stream, so that the CuMetallurgy_Flow function returns the Cu mass and Cu percentage in the blister Cu product.

def CuMetallurgy_Flow(
    Copper_input,
    Gas_input,
    SiO2_input,
    Gas_input_converter,
    SiO2_input_converter,
    Gas_input_ConverterForBlister,
    Smelter_T,
    Smelter_P,
    Converter_T,
    Converter_P,
    ConverterForBlister_T,
    ConverterForBlister_P
):
    Slag_output_smelter, Matte_output_smelter, Off_gas_smelter = Smelter(
        Copper_input, Gas_input, SiO2_input, Smelter_T, Smelter_P
    )

    Matte_output_converter, SiO2_output_converter, Gas_output_converter = Converter(
        Gas_input_converter, SiO2_input_converter, Matte_output_smelter, Converter_T, Converter_P
    )

    ConverterForBlister_result, \
    Copper_liq_speiss_output_ConverterForBlister, \
    Matte_output_ConverterForBlister, \
    Spinel_output_ConverterForBlister, \
    Slag_output_ConverterForBlister, \
    Gas_output_ConverterForBlister = ConverterForBlister(
        Gas_input_ConverterForBlister,
        Matte_output_converter,
        ConverterForBlister_T,
        ConverterForBlister_P
    )

    try:
        # Finding the index of Cu in the phase components from the Cu_liq_speiss stream from the output of ConverterFor Blister
        Cu_index = next(
            i for i, apc in enumerate(Copper_liq_speiss_output_ConverterForBlister.A_pcs)
            if apc.pc == 'Cu'
        )

        # After finding Cu index, then calculation of the amount of Cu formed and also the wt% of Cu in Cu_liq_speiss stream
        Cu_in_Cu_liq_speiss = Copper_liq_speiss_output_ConverterForBlister.A_pcs[Cu_index].A
        Cu_pct_in_Cu_liq_speiss = 100 * Copper_liq_speiss_output_ConverterForBlister.A_pcs[Cu_index].A / Copper_liq_speiss_output_ConverterForBlister.A
    except Exception:
        Cu_in_Cu_liq_speiss = 0
        Cu_pct_in_Cu_liq_speiss = 0

    return Cu_in_Cu_liq_speiss, Cu_pct_in_Cu_liq_speissCode language: PHP (php)

Application of the integrated process model

To illustrate the CuMetallurgy_Flow process model, input streams are first defined for the smelter, converter, and converter for blister, and each unit is assigned operating conditions of 1250 °C and 2.6 bar. These streams and conditions are then passed to the CuMetallurgy_Flow function, which returns the copper mass in kilograms and the copper weight percent in the blister Cu stream.

# Smelter streams
Copper_input = casc.create_st_cfs("Copper_input", ["CuFeS2", "H2O"], [920, 80], T=25.0, P=1.0)
Gas_input = casc.create_st_cfs("Gas_input", ["O2", "N2"], [294.032, 385.968], T=25.0, P=2.6)
SiO2_input = casc.create_st_cfs("SiO2_input", ["SiO2"], [100], T=25.0, P=1.0)

# Converter streams
SiO2_input_converter = casc.create_st_cfs("SiO2_input_cnv", ["SiO2"], [17], T=25.0, P=1.0)
Gas_input_converter = casc.create_st_cfs("Gas_input_converter", ["O2", "N2"], [25.944, 34.056], T=25.0, P=2.6)

# ConverterForBlister
Gas_input_ConverterForBlister = casc.create_st_cfs(
    "Gas_input_ConverterForBlister", ["O2", "N2"], [43.24, 56.76], T=25.0, P=2.6
)

Cu_in_Cu_liq_speiss, Cu_pct_in_Cu_liq_speiss = CuMetallurgy_Flow(
    Copper_input,
    Gas_input,
    SiO2_input,
    Gas_input_converter,
    SiO2_input_converter,
    Gas_input_ConverterForBlister,
    Smelter_T=1250,
    Smelter_P=2.6,
    Converter_T=1250,
    Converter_P=2.6,
    ConverterForBlister_T=1250,
    ConverterForBlister_P=2.6
)

print(f"Cu in Blister: {Cu_in_Cu_liq_speiss:.2f} kg")
print(f"Cu wt% in Blister: {Cu_pct_in_Cu_liq_speiss:.2f} %")Code language: PHP (php)

And this results in anticipating the amount of Cu in Blister produced, and also the Cu wt% as,

Expected output (example)

Cu in Blister: 211.97 kg
Cu wt% in Blister: 94.31 %Code language: CSS (css)

Conclusion

In this blog post, an integrated process model for the primary production of copper from CuFeS₂ (Chalcopyrite) has been developed. This model will form the foundation for upcoming blog posts, where we will utilize this process model to address Cu metallurgical challenges-optimizing process steps, evaluating alternative scenarios, and predicting the outcome under varying conditions.

Integrated process modular setups simplify the addition of new steps or processes (scalability), and could aid in the optimisation of Cu recovery while also reducing slag losses and emissions. Engineers can vary parameters systematically to enhance product quality, efficiency, and sustainability across metallurgy and beyond, including for example the areas of cement or chemicals production.

Do you want to do this? The full script can be downloaded from our repository of ChemApp for Python examples.

Outlook (Part II)

After creating integrated process model, the upcoming posts will employ this process model, as we produce copper, evaluate alternate case scenario, and predict outcomes. In the Part II of this blog post series, we will explore how process parameter optimization can be performed to maximize Cu output in Blister. Stay tuned for Part II….