UKy Flowsheet Tutorial#
This tutorial will show how to build, initialize, and simulate the West Kentucky No.13 Coal Refuse flowsheet. The inputs into this flowsheet are case study-specific, so users should not expect the flowsheet to solve if the values are significantly altered.
Useful Links:
Public Github Repository: prommis/prommis
UKy Flowsheet Code: prommis/prommis

Step 1: Import the necessary tools#
# Import the essentials from Pyomo
from pyomo.environ import (
ConcreteModel,
Constraint,
SolverFactory,
Suffix,
TransformationFactory,
Var,
units,
)
from pyomo.network import Arc, SequentialDecomposition
from pyomo.util.check_units import assert_units_consistent
# Import the essentials from IDAES
from idaes.models.properties.modular_properties.base.generic_property import (
GenericParameterBlock,
)
from idaes.models_extra.power_generation.properties.natural_gas_PR import (
EosType,
get_prop,
)
from idaes.core import (
FlowDirection,
FlowsheetBlock,
MaterialBalanceType,
MomentumBalanceType,
)
import idaes.logger as idaeslog
# Import initializtion and diagnostic tools from IDAES
from idaes.core.initialization import BlockTriangularizationInitializer
from idaes.core.util.initialization import propagate_state
from idaes.core.util.model_diagnostics import DiagnosticsToolbox
from idaes.core.util.model_statistics import degrees_of_freedom
# Import unit models from IDAES
from idaes.models.unit_models.feed import Feed, FeedInitializer
from idaes.models.unit_models.mixer import (
Mixer,
MixingType,
MomentumMixingType,
MixerInitializer,
)
from idaes.models.unit_models.mscontactor import MSContactor, MSContactorInitializer
from idaes.models.unit_models.product import Product, ProductInitializer
from idaes.models.unit_models.separator import (
EnergySplittingType,
Separator,
SplittingType,
SeparatorInitializer,
)
from idaes.models.unit_models.solid_liquid import SLSeparator
# Import the UKy-specific unit and property models
from prommis.leaching.leach_reactions import CoalRefuseLeachingReactions
from prommis.leaching.leach_solids_properties import CoalRefuseParameters
from prommis.leaching.leach_solution_properties import LeachSolutionParameters
from prommis.precipitate.precipitate_liquid_properties import AqueousParameter
from prommis.precipitate.precipitate_solids_properties import PrecipitateParameters
from prommis.precipitate.precipitator import Precipitator
from prommis.roasting.ree_oxalate_roaster import REEOxalateRoaster
from prommis.solvent_extraction.ree_og_distribution import REESolExOgParameters
from prommis.solvent_extraction.solvent_extraction import SolventExtraction
# Set up logger
_log = idaeslog.getLogger(__name__)
Step 2 Flowsheet building#
Step 2.1: Create Flowsheet#
Start by creating a pyomo model and a flowsheet.
m = ConcreteModel()
m.fs = FlowsheetBlock(dynamic=False)
Then begin assembling the unit, property, and reaction models section-by-section. These variables will be created in chronological order - beginning with the leaching section of the flowsheet and concluding with the product roasting section.
Step 2.2 Create variables for the leaching section#
Specify the necessary unit, property, and reaction models for the leaching section.
# Leaching property models
m.fs.leach_soln = LeachSolutionParameters()
m.fs.coal = CoalRefuseParameters()
# Leaching reaction model
m.fs.leach_rxns = CoalRefuseLeachingReactions()
# Leaching unit model
m.fs.leach = MSContactor(
number_of_finite_elements=2,
streams={
"liquid": {
"property_package": m.fs.leach_soln,
"has_energy_balance": False,
"has_pressure_balance": False,
},
"solid": {
"property_package": m.fs.coal,
"has_energy_balance": False,
"has_pressure_balance": False,
},
},
heterogeneous_reactions=m.fs.leach_rxns,
)
# Define leach tank volume
m.fs.leach.volume = Var(
m.fs.time,
m.fs.leach.elements,
initialize=1,
units=units.litre,
doc="Volume of each finite element.",
)
# Define constraint for heterogeneous reaction extent
def rule_heterogeneous_reaction_extent(b, t, s, r):
return (
b.heterogeneous_reaction_extent[t, s, r]
== b.heterogeneous_reactions[t, s].reaction_rate[r] * b.volume[t, s]
)
m.fs.leach.heterogeneous_reaction_extent_constraint = Constraint(
m.fs.time,
m.fs.leach.elements,
m.fs.leach_rxns.reaction_idx,
rule=rule_heterogeneous_reaction_extent,
)
# Solid-liquid separator used to approximate a filter press
m.fs.sl_sep1 = SLSeparator(
solid_property_package=m.fs.coal,
liquid_property_package=m.fs.leach_soln,
material_balance_type=MaterialBalanceType.componentTotal,
# Ignore momentum balance since the property package does not have pressure or momentum terms
momentum_balance_type=MomentumBalanceType.none,
# Ignore energy split basis since the property package does not have temperature terms
energy_split_basis=EnergySplittingType.none,
)
# Recycle loop mixer
m.fs.leach_mixer = Mixer(
property_package=m.fs.leach_soln,
num_inlets=3,
inlet_list=["load_recycle", "scrub_recycle", "feed"],
material_balance_type=MaterialBalanceType.componentTotal,
# Ignore mixing type since the property package does not have enthalpy terms
energy_mixing_type=MixingType.none,
# Ignore momentum mixing since the property package does not have pressure or momentum terms
momentum_mixing_type=MomentumMixingType.none,
)
# Define inlets into the flowsheet
m.fs.leach_liquid_feed = Feed(property_package=m.fs.leach_soln)
m.fs.leach_solid_feed = Feed(property_package=m.fs.coal)
# Define outlets from the flowsheet
m.fs.leach_filter_cake = Product(property_package=m.fs.coal)
m.fs.leach_filter_cake_liquid = Product(property_package=m.fs.leach_soln)
Step 2.3 Create variables for the solvent extraction section#
Specify the necessary unit, property, and reaction models for the solvent extraction section.
# Solvent extraction property models
m.fs.prop_o = REESolExOgParameters()
m.fs.rougher_org_make_up = Feed(property_package=m.fs.prop_o)
m.fs.solex_rougher_load = SolventExtraction(
number_of_finite_elements=3,
dynamic=False,
aqueous_stream={
"property_package": m.fs.leach_soln,
"flow_direction": FlowDirection.forward,
"has_energy_balance": False,
"has_pressure_balance": False,
},
organic_stream={
"property_package": m.fs.prop_o,
"flow_direction": FlowDirection.backward,
"has_energy_balance": False,
"has_pressure_balance": False,
},
aqueous_to_organic=True,
)
# Define partition coefficients for each finite element in solex_rougher_load
m.fs.solex_rougher_load.partition_coefficient[1, "aqueous", "organic", "Al"] = 5.2 / 100
m.fs.solex_rougher_load.partition_coefficient[1, "aqueous", "organic", "Ca"] = 3.0 / 100
m.fs.solex_rougher_load.partition_coefficient[1, "aqueous", "organic", "Fe"] = (
24.7 / 100
)
m.fs.solex_rougher_load.partition_coefficient[1, "aqueous", "organic", "Sc"] = (
99.9 / 100
)
m.fs.solex_rougher_load.partition_coefficient[1, "aqueous", "organic", "Y"] = 99.9 / 100
m.fs.solex_rougher_load.partition_coefficient[1, "aqueous", "organic", "La"] = (
32.4 / 100
)
m.fs.solex_rougher_load.partition_coefficient[1, "aqueous", "organic", "Ce"] = (
58.2 / 100
)
m.fs.solex_rougher_load.partition_coefficient[1, "aqueous", "organic", "Pr"] = (
58.2 / 100
)
m.fs.solex_rougher_load.partition_coefficient[1, "aqueous", "organic", "Nd"] = (
87.6 / 100
)
m.fs.solex_rougher_load.partition_coefficient[1, "aqueous", "organic", "Sm"] = (
99.9 / 100
)
m.fs.solex_rougher_load.partition_coefficient[1, "aqueous", "organic", "Gd"] = (
69.8 / 100
)
m.fs.solex_rougher_load.partition_coefficient[1, "aqueous", "organic", "Dy"] = (
96.6 / 100
)
m.fs.solex_rougher_load.partition_coefficient[2, "aqueous", "organic", "Al"] = 4.9 / 100
m.fs.solex_rougher_load.partition_coefficient[2, "aqueous", "organic", "Ca"] = (
12.3 / 100
)
m.fs.solex_rougher_load.partition_coefficient[2, "aqueous", "organic", "Fe"] = 6.4 / 100
m.fs.solex_rougher_load.partition_coefficient[2, "aqueous", "organic", "Sc"] = (
16.7 / 100
)
m.fs.solex_rougher_load.partition_coefficient[2, "aqueous", "organic", "Y"] = 99.9 / 100
m.fs.solex_rougher_load.partition_coefficient[2, "aqueous", "organic", "La"] = (
23.2 / 100
)
m.fs.solex_rougher_load.partition_coefficient[2, "aqueous", "organic", "Ce"] = (
24.9 / 100
)
m.fs.solex_rougher_load.partition_coefficient[2, "aqueous", "organic", "Pr"] = (
15.1 / 100
)
m.fs.solex_rougher_load.partition_coefficient[2, "aqueous", "organic", "Nd"] = (
99.9 / 100
)
m.fs.solex_rougher_load.partition_coefficient[2, "aqueous", "organic", "Sm"] = (
99.9 / 100
)
m.fs.solex_rougher_load.partition_coefficient[2, "aqueous", "organic", "Gd"] = 7.6 / 100
m.fs.solex_rougher_load.partition_coefficient[2, "aqueous", "organic", "Dy"] = 5.0 / 100
m.fs.solex_rougher_load.partition_coefficient[3, "aqueous", "organic", "Al"] = 4.9 / 100
m.fs.solex_rougher_load.partition_coefficient[3, "aqueous", "organic", "Ca"] = (
12.3 / 100
)
m.fs.solex_rougher_load.partition_coefficient[3, "aqueous", "organic", "Fe"] = 6.4 / 100
m.fs.solex_rougher_load.partition_coefficient[3, "aqueous", "organic", "Sc"] = (
16.7 / 100
)
m.fs.solex_rougher_load.partition_coefficient[3, "aqueous", "organic", "Y"] = 99.9 / 100
m.fs.solex_rougher_load.partition_coefficient[3, "aqueous", "organic", "La"] = (
23.2 / 100
)
m.fs.solex_rougher_load.partition_coefficient[3, "aqueous", "organic", "Ce"] = (
24.9 / 100
)
m.fs.solex_rougher_load.partition_coefficient[3, "aqueous", "organic", "Pr"] = (
15.1 / 100
)
m.fs.solex_rougher_load.partition_coefficient[3, "aqueous", "organic", "Nd"] = (
99.9 / 100
)
m.fs.solex_rougher_load.partition_coefficient[3, "aqueous", "organic", "Sm"] = (
99.9 / 100
)
m.fs.solex_rougher_load.partition_coefficient[3, "aqueous", "organic", "Gd"] = 7.6 / 100
m.fs.solex_rougher_load.partition_coefficient[3, "aqueous", "organic", "Dy"] = 5.0 / 100
# Dilute HCl feed
m.fs.acid_feed1 = Feed(property_package=m.fs.leach_soln)
m.fs.solex_rougher_scrub = SolventExtraction(
number_of_finite_elements=1,
dynamic=False,
aqueous_stream={
"property_package": m.fs.leach_soln,
"flow_direction": FlowDirection.backward,
"has_energy_balance": False,
"has_pressure_balance": False,
},
organic_stream={
"property_package": m.fs.prop_o,
"flow_direction": FlowDirection.forward,
"has_energy_balance": False,
"has_pressure_balance": False,
},
aqueous_to_organic=False,
)
# Define partition coefficients for each finite element in solex_rougher_scrub
m.fs.solex_rougher_scrub.partition_coefficient[1, "aqueous", "organic", "Al"] = (
100 - 0.12
) / 100
m.fs.solex_rougher_scrub.partition_coefficient[1, "aqueous", "organic", "Ca"] = (
100 - 0.55
) / 100
m.fs.solex_rougher_scrub.partition_coefficient[1, "aqueous", "organic", "Fe"] = (
100 - 0.007
) / 100
m.fs.solex_rougher_scrub.partition_coefficient[1, "aqueous", "organic", "Sc"] = (
100 - 99.9
) / 100
m.fs.solex_rougher_scrub.partition_coefficient[1, "aqueous", "organic", "Y"] = (
100 - 99.9
) / 100
m.fs.solex_rougher_scrub.partition_coefficient[1, "aqueous", "organic", "La"] = (
100 - 99.8
) / 100
m.fs.solex_rougher_scrub.partition_coefficient[1, "aqueous", "organic", "Ce"] = (
100 - 99.9
) / 100
m.fs.solex_rougher_scrub.partition_coefficient[1, "aqueous", "organic", "Pr"] = (
100 - 99.9
) / 100
m.fs.solex_rougher_scrub.partition_coefficient[1, "aqueous", "organic", "Nd"] = (
100 - 99.9
) / 100
m.fs.solex_rougher_scrub.partition_coefficient[1, "aqueous", "organic", "Sm"] = (
100 - 99.9
) / 100
m.fs.solex_rougher_scrub.partition_coefficient[1, "aqueous", "organic", "Gd"] = (
100 - 99.9
) / 100
m.fs.solex_rougher_scrub.partition_coefficient[1, "aqueous", "organic", "Dy"] = (
100 - 99.9
) / 100
m.fs.acid_feed2 = Feed(property_package=m.fs.leach_soln)
m.fs.solex_rougher_strip = SolventExtraction(
number_of_finite_elements=2,
dynamic=False,
aqueous_stream={
"property_package": m.fs.leach_soln,
"flow_direction": FlowDirection.backward,
"has_energy_balance": False,
"has_pressure_balance": False,
},
organic_stream={
"property_package": m.fs.prop_o,
"flow_direction": FlowDirection.forward,
"has_energy_balance": False,
"has_pressure_balance": False,
},
aqueous_to_organic=False,
)
# Define partition coefficients for each finite element in solex_rougher_strip
m.fs.solex_rougher_strip.partition_coefficient[:, "aqueous", "organic", "Al"] = (
100 - 0.5
) / 100
m.fs.solex_rougher_strip.partition_coefficient[:, "aqueous", "organic", "Ca"] = (
100 - 0.5
) / 100
m.fs.solex_rougher_strip.partition_coefficient[:, "aqueous", "organic", "Fe"] = (
100 - 0.5
) / 100
m.fs.solex_rougher_strip.partition_coefficient[:, "aqueous", "organic", "Sc"] = (
100 - 98.5
) / 100
m.fs.solex_rougher_strip.partition_coefficient[:, "aqueous", "organic", "Y"] = (
100 - 0.5
) / 100
m.fs.solex_rougher_strip.partition_coefficient[:, "aqueous", "organic", "La"] = (
100 - 0.5
) / 100
m.fs.solex_rougher_strip.partition_coefficient[:, "aqueous", "organic", "Ce"] = (
100 - 0.5
) / 100
m.fs.solex_rougher_strip.partition_coefficient[:, "aqueous", "organic", "Pr"] = (
100 - 0.5
) / 100
m.fs.solex_rougher_strip.partition_coefficient[:, "aqueous", "organic", "Nd"] = (
100 - 0.5
) / 100
m.fs.solex_rougher_strip.partition_coefficient[:, "aqueous", "organic", "Sm"] = (
100 - 0.5
) / 100
m.fs.solex_rougher_strip.partition_coefficient[:, "aqueous", "organic", "Gd"] = (
100 - 0.5
) / 100
m.fs.solex_rougher_strip.partition_coefficient[:, "aqueous", "organic", "Dy"] = (
100 - 0.5
) / 100
# Separator for organic stream
m.fs.rougher_sep = Separator(
property_package=m.fs.prop_o,
outlet_list=["recycle", "purge"],
split_basis=SplittingType.totalFlow,
material_balance_type=MaterialBalanceType.componentTotal,
momentum_balance_type=MomentumBalanceType.none,
energy_split_basis=EnergySplittingType.none,
)
m.fs.rougher_mixer = Mixer(
property_package=m.fs.prop_o,
num_inlets=2,
inlet_list=["make_up", "recycle"],
material_balance_type=MaterialBalanceType.componentTotal,
energy_mixing_type=MixingType.none,
momentum_mixing_type=MomentumMixingType.none,
)
# Separators for aqueous streams
m.fs.load_sep = Separator(
property_package=m.fs.leach_soln,
outlet_list=["recycle", "purge"],
split_basis=SplittingType.totalFlow,
material_balance_type=MaterialBalanceType.componentTotal,
momentum_balance_type=MomentumBalanceType.none,
energy_split_basis=EnergySplittingType.none,
)
m.fs.scrub_sep = Separator(
property_package=m.fs.leach_soln,
outlet_list=["recycle", "purge"],
split_basis=SplittingType.totalFlow,
material_balance_type=MaterialBalanceType.componentTotal,
momentum_balance_type=MomentumBalanceType.none,
energy_split_basis=EnergySplittingType.none,
)
m.fs.sc_circuit_purge = Product(property_package=m.fs.prop_o)
m.fs.solex_cleaner_load = SolventExtraction(
number_of_finite_elements=3,
dynamic=False,
aqueous_stream={
"property_package": m.fs.leach_soln,
"flow_direction": FlowDirection.forward,
"has_energy_balance": False,
"has_pressure_balance": False,
},
organic_stream={
"property_package": m.fs.prop_o,
"flow_direction": FlowDirection.backward,
"has_energy_balance": False,
"has_pressure_balance": False,
},
aqueous_to_organic=True,
)
# Define partition coefficients for each finite element in solex_cleaner_load
m.fs.solex_cleaner_load.partition_coefficient[:, "aqueous", "organic", "Al"] = 3.6 / 100
m.fs.solex_cleaner_load.partition_coefficient[:, "aqueous", "organic", "Ca"] = 3.7 / 100
m.fs.solex_cleaner_load.partition_coefficient[:, "aqueous", "organic", "Fe"] = 2.1 / 100
m.fs.solex_cleaner_load.partition_coefficient[:, "aqueous", "organic", "Sc"] = (
99.9 / 100
)
m.fs.solex_cleaner_load.partition_coefficient[:, "aqueous", "organic", "Y"] = 99.9 / 100
m.fs.solex_cleaner_load.partition_coefficient[:, "aqueous", "organic", "La"] = (
75.2 / 100
)
m.fs.solex_cleaner_load.partition_coefficient[:, "aqueous", "organic", "Ce"] = (
95.7 / 100
)
m.fs.solex_cleaner_load.partition_coefficient[:, "aqueous", "organic", "Pr"] = (
96.5 / 100
)
m.fs.solex_cleaner_load.partition_coefficient[:, "aqueous", "organic", "Nd"] = (
99.2 / 100
)
m.fs.solex_cleaner_load.partition_coefficient[:, "aqueous", "organic", "Sm"] = (
99.9 / 100
)
m.fs.solex_cleaner_load.partition_coefficient[:, "aqueous", "organic", "Gd"] = (
98.6 / 100
)
m.fs.solex_cleaner_load.partition_coefficient[:, "aqueous", "organic", "Dy"] = (
99.9 / 100
)
m.fs.solex_cleaner_strip = SolventExtraction(
number_of_finite_elements=3,
dynamic=False,
aqueous_stream={
"property_package": m.fs.leach_soln,
"flow_direction": FlowDirection.backward,
"has_energy_balance": False,
"has_pressure_balance": False,
},
organic_stream={
"property_package": m.fs.prop_o,
"flow_direction": FlowDirection.forward,
"has_energy_balance": False,
"has_pressure_balance": False,
},
aqueous_to_organic=False,
)
m.fs.solex_cleaner_strip.partition_coefficient[:, "aqueous", "organic", "Al"] = (
100 - 0.5
) / 100
m.fs.solex_cleaner_strip.partition_coefficient[:, "aqueous", "organic", "Ca"] = (
100 - 0.5
) / 100
m.fs.solex_cleaner_strip.partition_coefficient[:, "aqueous", "organic", "Fe"] = (
100 - 5
) / 100
m.fs.solex_cleaner_strip.partition_coefficient[:, "aqueous", "organic", "Sc"] = (
100 - 98.5
) / 100
m.fs.solex_cleaner_strip.partition_coefficient[:, "aqueous", "organic", "Y"] = (
100 - 0.5
) / 100
m.fs.solex_cleaner_strip.partition_coefficient[:, "aqueous", "organic", "La"] = (
100 - 0.5
) / 100
m.fs.solex_cleaner_strip.partition_coefficient[:, "aqueous", "organic", "Ce"] = (
100 - 0.5
) / 100
m.fs.solex_cleaner_strip.partition_coefficient[:, "aqueous", "organic", "Pr"] = (
100 - 0.5
) / 100
m.fs.solex_cleaner_strip.partition_coefficient[:, "aqueous", "organic", "Nd"] = (
100 - 0.5
) / 100
m.fs.solex_cleaner_strip.partition_coefficient[:, "aqueous", "organic", "Sm"] = (
100 - 0.5
) / 100
m.fs.solex_cleaner_strip.partition_coefficient[:, "aqueous", "organic", "Gd"] = (
100 - 0.5
) / 100
m.fs.solex_cleaner_strip.partition_coefficient[:, "aqueous", "organic", "Dy"] = (
100 - 0.5
) / 100
m.fs.cleaner_org_make_up = Feed(property_package=m.fs.prop_o)
# Separator and mixer for organic stream
m.fs.cleaner_sep = Separator(
property_package=m.fs.prop_o,
outlet_list=["recycle", "purge"],
split_basis=SplittingType.totalFlow,
material_balance_type=MaterialBalanceType.componentTotal,
momentum_balance_type=MomentumBalanceType.none,
energy_split_basis=EnergySplittingType.none,
)
m.fs.cleaner_mixer = Mixer(
property_package=m.fs.prop_o,
num_inlets=2,
inlet_list=["make_up", "recycle"],
material_balance_type=MaterialBalanceType.componentTotal,
energy_mixing_type=MixingType.none,
momentum_mixing_type=MomentumMixingType.none,
)
m.fs.leach_sx_mixer = Mixer(
property_package=m.fs.leach_soln,
num_inlets=2,
inlet_list=["leach", "cleaner"],
material_balance_type=MaterialBalanceType.componentTotal,
energy_mixing_type=MixingType.none,
momentum_mixing_type=MomentumMixingType.none,
)
m.fs.acid_feed3 = Feed(property_package=m.fs.leach_soln)
m.fs.cleaner_purge = Product(property_package=m.fs.prop_o)
Step 2.3 Create variables for the precipitation section#
Specify the necessary unit, property, and reaction models for the precipitation section.
# Precipitation property packages
m.fs.properties_aq = AqueousParameter()
m.fs.properties_solid = PrecipitateParameters()
# Precipitation unit model
m.fs.precipitator = Precipitator(
property_package_aqueous=m.fs.properties_aq,
property_package_precipitate=m.fs.properties_solid,
)
# Solid-liquid separator used to approximate a filter press
m.fs.sl_sep2 = SLSeparator(
solid_property_package=m.fs.properties_solid,
liquid_property_package=m.fs.leach_soln,
material_balance_type=MaterialBalanceType.componentTotal,
momentum_balance_type=MomentumBalanceType.none,
energy_split_basis=EnergySplittingType.none,
)
m.fs.precip_sep = Separator(
property_package=m.fs.leach_soln,
outlet_list=["recycle", "purge"],
split_basis=SplittingType.totalFlow,
material_balance_type=MaterialBalanceType.componentTotal,
momentum_balance_type=MomentumBalanceType.none,
energy_split_basis=EnergySplittingType.none,
)
m.fs.precip_sx_mixer = Mixer(
property_package=m.fs.leach_soln,
num_inlets=2,
inlet_list=["precip", "rougher"],
material_balance_type=MaterialBalanceType.componentTotal,
energy_mixing_type=MixingType.none,
momentum_mixing_type=MomentumMixingType.none,
)
# Define outlets from the flowsheet
m.fs.precip_purge = Product(property_package=m.fs.properties_aq)
Step 2.3 Create variables for the product roaster section#
Specify the necessary unit, property, and reaction models for the roaster section.
# Define the relevant gas species
gas_species = {"O2", "H2O", "CO2", "N2"}
# Roaster property packages
m.fs.prop_gas = GenericParameterBlock(
**get_prop(gas_species, ["Vap"], EosType.IDEAL),
doc="gas property",
)
m.fs.prop_solid = PrecipitateParameters()
# Roaster unit model
m.fs.roaster = REEOxalateRoaster(
property_package_gas=m.fs.prop_gas,
property_package_precipitate_solid=m.fs.prop_solid,
property_package_precipitate_liquid=m.fs.properties_aq,
has_holdup=False,
has_heat_transfer=True,
has_pressure_change=True,
)
Step 2.4 Connect the unit models#
Next, use Pyomo arcs as streams to connect the units as portrayed in the flowsheet image above.
m.fs.leaching_sol_feed = Arc(
source=m.fs.leach_solid_feed.outlet, destination=m.fs.leach.solid_inlet
)
m.fs.leaching_liq_feed = Arc(
source=m.fs.leach_liquid_feed.outlet, destination=m.fs.leach_mixer.feed
)
m.fs.leaching_feed_mixture = Arc(
source=m.fs.leach_mixer.outlet, destination=m.fs.leach.liquid_inlet
)
m.fs.leaching_solid_outlet = Arc(
source=m.fs.leach.solid_outlet, destination=m.fs.sl_sep1.solid_inlet
)
m.fs.leaching_liquid_outlet = Arc(
source=m.fs.leach.liquid_outlet, destination=m.fs.sl_sep1.liquid_inlet
)
m.fs.sl_sep1_solid_outlet = Arc(
source=m.fs.sl_sep1.solid_outlet, destination=m.fs.leach_filter_cake.inlet
)
m.fs.sl_sep1_retained_liquid_outlet = Arc(
source=m.fs.sl_sep1.retained_liquid_outlet,
destination=m.fs.leach_filter_cake_liquid.inlet,
)
m.fs.sl_sep1_liquid_outlet = Arc(
source=m.fs.sl_sep1.recovered_liquid_outlet,
destination=m.fs.leach_sx_mixer.leach,
)
m.fs.sx_rougher_load_aq_feed = Arc(
source=m.fs.leach_sx_mixer.outlet,
destination=m.fs.solex_rougher_load.mscontactor.aqueous_inlet,
)
m.fs.sx_rougher_org_feed = Arc(
source=m.fs.rougher_org_make_up.outlet, destination=m.fs.rougher_mixer.make_up
)
m.fs.sx_rougher_mixed_org_recycle = Arc(
source=m.fs.rougher_mixer.outlet,
destination=m.fs.solex_rougher_load.mscontactor.organic_inlet,
)
m.fs.sx_rougher_load_aq_outlet = Arc(
source=m.fs.solex_rougher_load.mscontactor.aqueous_outlet,
destination=m.fs.load_sep.inlet,
)
m.fs.sx_rougher_load_aq_recycle = Arc(
source=m.fs.load_sep.recycle, destination=m.fs.leach_mixer.load_recycle
)
m.fs.sx_rougher_load_org_outlet = Arc(
source=m.fs.solex_rougher_load.mscontactor.organic_outlet,
destination=m.fs.solex_rougher_scrub.mscontactor.organic_inlet,
)
m.fs.sx_rougher_scrub_acid_feed = Arc(
source=m.fs.acid_feed1.outlet,
destination=m.fs.solex_rougher_scrub.mscontactor.aqueous_inlet,
)
m.fs.sx_rougher_scrub_aq_outlet = Arc(
source=m.fs.solex_rougher_scrub.mscontactor.aqueous_outlet,
destination=m.fs.scrub_sep.inlet,
)
m.fs.sx_rougher_scrub_aq_recycle = Arc(
source=m.fs.scrub_sep.recycle, destination=m.fs.leach_mixer.scrub_recycle
)
m.fs.sx_rougher_scrub_org_outlet = Arc(
source=m.fs.solex_rougher_scrub.mscontactor.organic_outlet,
destination=m.fs.solex_rougher_strip.mscontactor.organic_inlet,
)
m.fs.sx_rougher_strip_acid_feed = Arc(
source=m.fs.acid_feed2.outlet,
destination=m.fs.solex_rougher_strip.mscontactor.aqueous_inlet,
)
m.fs.sx_rougher_strip_org_outlet = Arc(
source=m.fs.solex_rougher_strip.mscontactor.organic_outlet,
destination=m.fs.rougher_sep.inlet,
)
m.fs.sx_rougher_strip_org_purge = Arc(
source=m.fs.rougher_sep.purge, destination=m.fs.sc_circuit_purge.inlet
)
m.fs.sx_rougher_strip_org_recycle = Arc(
source=m.fs.rougher_sep.recycle, destination=m.fs.rougher_mixer.recycle
)
m.fs.sx_rougher_strip_aq_outlet = Arc(
source=m.fs.solex_rougher_strip.mscontactor.aqueous_outlet,
destination=m.fs.precip_sx_mixer.rougher,
)
m.fs.sx_cleaner_load_aq_feed = Arc(
source=m.fs.precip_sx_mixer.outlet,
destination=m.fs.solex_cleaner_load.mscontactor.aqueous_inlet,
)
m.fs.sx_cleaner_org_feed = Arc(
source=m.fs.cleaner_org_make_up.outlet, destination=m.fs.cleaner_mixer.make_up
)
m.fs.sx_cleaner_mixed_org_recycle = Arc(
source=m.fs.cleaner_mixer.outlet,
destination=m.fs.solex_cleaner_load.mscontactor.organic_inlet,
)
m.fs.sx_cleaner_load_aq_outlet = Arc(
source=m.fs.solex_cleaner_load.mscontactor.aqueous_outlet,
destination=m.fs.leach_sx_mixer.cleaner,
)
m.fs.sx_cleaner_strip_acid_feed = Arc(
source=m.fs.acid_feed3.outlet,
destination=m.fs.solex_cleaner_strip.mscontactor.aqueous_inlet,
)
m.fs.sx_cleaner_load_org_outlet = Arc(
source=m.fs.solex_cleaner_load.mscontactor.organic_outlet,
destination=m.fs.solex_cleaner_strip.mscontactor.organic_inlet,
)
m.fs.sx_cleaner_strip_org_outlet = Arc(
source=m.fs.solex_cleaner_strip.mscontactor.organic_outlet,
destination=m.fs.cleaner_sep.inlet,
)
m.fs.sx_cleaner_strip_org_purge = Arc(
source=m.fs.cleaner_sep.purge, destination=m.fs.cleaner_purge.inlet
)
m.fs.sx_cleaner_strip_org_recycle = Arc(
source=m.fs.cleaner_sep.recycle, destination=m.fs.cleaner_mixer.recycle
)
m.fs.sx_cleaner_strip_aq_outlet = Arc(
source=m.fs.solex_cleaner_strip.mscontactor.aqueous_outlet,
destination=m.fs.precipitator.aqueous_inlet,
)
m.fs.precip_solid_outlet = Arc(
source=m.fs.precipitator.precipitate_outlet,
destination=m.fs.sl_sep2.solid_inlet,
)
m.fs.precip_aq_outlet = Arc(
source=m.fs.precipitator.aqueous_outlet, destination=m.fs.sl_sep2.liquid_inlet
)
m.fs.sl_sep2_solid_outlet = Arc(
source=m.fs.sl_sep2.solid_outlet, destination=m.fs.roaster.solid_inlet
)
m.fs.sl_sep2_liquid_outlet = Arc(
source=m.fs.sl_sep2.recovered_liquid_outlet, destination=m.fs.precip_sep.inlet
)
m.fs.sl_sep2_retained_liquid_outlet = Arc(
source=m.fs.sl_sep2.retained_liquid_outlet,
destination=m.fs.roaster.liquid_inlet,
)
m.fs.sl_sep2_aq_purge = Arc(
source=m.fs.precip_sep.purge, destination=m.fs.precip_purge.inlet
)
m.fs.sl_sep2_aq_recycle = Arc(
source=m.fs.precip_sep.recycle,
destination=m.fs.precip_sx_mixer.precip,
)
TransformationFactory("network.expand_arcs").apply_to(m)
Step 2.5 Apply scaling#
In order for the flowsheet to solve, variables will need to be scaled appropriately. While variables often have a default scaling set, it is important to re-scale those with poor initial scaling in this flowsheet as there are many trace components resulting in variables with very small magnitudes.
def set_scaling(m):
m.scaling_factor = Suffix(direction=Suffix.EXPORT)
# Create sets to target variables with these specific components
aqueous_component_set = [
"H2O",
"H",
"HSO4",
"SO4",
"Cl",
"Sc",
"Y",
"La",
"Ce",
"Pr",
"Nd",
"Sm",
"Gd",
"Dy",
"Al",
"Ca",
"Fe",
]
organic_component_set = [
"Sc",
"Y",
"La",
"Ce",
"Pr",
"Nd",
"Sm",
"Gd",
"Dy",
"Al",
"Ca",
"Fe",
]
for component in aqueous_component_set:
m.scaling_factor[m.fs.leach.liquid[0, 1].conc_mol_comp[component]] = 1e5
m.scaling_factor[m.fs.leach.liquid[0, 2].conc_mol_comp[component]] = 1e5
m.scaling_factor[m.fs.leach.liquid_inlet_state[0].conc_mol_comp[component]] = (
1e5
)
m.scaling_factor[
m.fs.leach_liquid_feed.properties[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.sl_sep1.liquid_inlet_state[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.sl_sep1.split.recovered_state[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.sl_sep1.split.retained_state[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.leach_filter_cake_liquid.properties[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[m.fs.leach.liquid_inlet_state[0].conc_mol_comp[component]] = (
1e5
)
m.scaling_factor[m.fs.leach.liquid_inlet_state[0].conc_mol_comp[component]] = (
1e5
)
m.scaling_factor[m.fs.leach.liquid_inlet_state[0].conc_mol_comp[component]] = (
1e5
)
m.scaling_factor[m.fs.leach.liquid_inlet_state[0].conc_mol_comp[component]] = (
1e5
)
m.scaling_factor[
m.fs.leach_mixer.load_recycle_state[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.leach_mixer.scrub_recycle_state[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[m.fs.leach_mixer.feed_state[0].conc_mol_comp[component]] = 1e5
m.scaling_factor[m.fs.leach_mixer.mixed_state[0].conc_mol_comp[component]] = 1e5
m.scaling_factor[
m.fs.solex_rougher_load.mscontactor.aqueous[0, 1].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_rougher_scrub.mscontactor.aqueous[0, 1].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_rougher_strip.mscontactor.aqueous[0, 1].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_rougher_load.mscontactor.aqueous[0, 1].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_rougher_load.mscontactor.aqueous[0, 2].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_rougher_load.mscontactor.aqueous[0, 3].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_rougher_load.mscontactor.aqueous_inlet_state[0].conc_mol_comp[
component
]
] = 1e5
m.scaling_factor[m.fs.acid_feed1.properties[0].conc_mol_comp[component]] = 1e5
m.scaling_factor[
m.fs.solex_rougher_scrub.mscontactor.aqueous[0, 1].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_rougher_scrub.mscontactor.aqueous_inlet_state[0].conc_mol_comp[
component
]
] = 1e5
m.scaling_factor[m.fs.acid_feed2.properties[0].conc_mol_comp[component]] = 1e5
m.scaling_factor[
m.fs.solex_rougher_strip.mscontactor.aqueous[0, 1].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_rougher_strip.mscontactor.aqueous[0, 2].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_rougher_strip.mscontactor.aqueous_inlet_state[0].conc_mol_comp[
component
]
] = 1e5
m.scaling_factor[
m.fs.solex_cleaner_load.mscontactor.aqueous[0, 1].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_cleaner_load.mscontactor.aqueous[0, 2].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_cleaner_load.mscontactor.aqueous[0, 3].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_cleaner_load.mscontactor.aqueous_inlet_state[0].conc_mol_comp[
component
]
] = 1e5
m.scaling_factor[
m.fs.leach_sx_mixer.leach_state[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.leach_sx_mixer.cleaner_state[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.leach_sx_mixer.mixed_state[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_cleaner_strip.mscontactor.aqueous[0, 1].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_cleaner_strip.mscontactor.aqueous[0, 2].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_cleaner_strip.mscontactor.aqueous[0, 3].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_cleaner_strip.mscontactor.aqueous_inlet_state[0].conc_mol_comp[
component
]
] = 1e5
m.scaling_factor[
m.fs.sl_sep2.liquid_inlet_state[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.sl_sep2.split.retained_state[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.sl_sep2.split.recovered_state[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[m.fs.load_sep.mixed_state[0].conc_mol_comp[component]] = 1e5
m.scaling_factor[m.fs.load_sep.recycle_state[0].conc_mol_comp[component]] = 1e5
m.scaling_factor[m.fs.load_sep.purge_state[0].conc_mol_comp[component]] = 1e5
m.scaling_factor[m.fs.scrub_sep.mixed_state[0].conc_mol_comp[component]] = 1e5
m.scaling_factor[m.fs.scrub_sep.recycle_state[0].conc_mol_comp[component]] = 1e5
m.scaling_factor[m.fs.scrub_sep.purge_state[0].conc_mol_comp[component]] = 1e5
m.scaling_factor[m.fs.precip_sep.mixed_state[0].conc_mol_comp[component]] = 1e5
m.scaling_factor[m.fs.precip_sep.recycle_state[0].conc_mol_comp[component]] = (
1e5
)
m.scaling_factor[m.fs.precip_sep.purge_state[0].conc_mol_comp[component]] = 1e5
m.scaling_factor[m.fs.precip_purge.properties[0].conc_mol_comp[component]] = 1e5
m.scaling_factor[
m.fs.precip_sx_mixer.precip_state[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.precip_sx_mixer.rougher_state[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.precip_sx_mixer.mixed_state[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[m.fs.acid_feed3.properties[0].conc_mol_comp[component]] = 1e5
m.scaling_factor[m.fs.precip_purge.properties[0].conc_mol_comp[component]] = 1
m.scaling_factor[
m.fs.precipitator.cv_aqueous.properties_in[0].conc_mol_comp[component]
] = 1
m.scaling_factor[
m.fs.precipitator.cv_aqueous.properties_out[0].conc_mol_comp[component]
] = 1
for component in organic_component_set:
m.scaling_factor[
m.fs.rougher_org_make_up.properties[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_rougher_load.mscontactor.organic[0, 1].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_rougher_load.mscontactor.organic[0, 2].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_rougher_load.mscontactor.organic[0, 3].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_rougher_load.mscontactor.organic_inlet_state[0].conc_mol_comp[
component
]
] = 1e5
m.scaling_factor[
m.fs.solex_rougher_scrub.mscontactor.organic[0, 1].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_rougher_scrub.mscontactor.organic_inlet_state[0].conc_mol_comp[
component
]
] = 1e5
m.scaling_factor[
m.fs.solex_rougher_strip.mscontactor.organic[0, 1].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_rougher_strip.mscontactor.organic[0, 2].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_rougher_strip.mscontactor.organic_inlet_state[0].conc_mol_comp[
component
]
] = 1e5
m.scaling_factor[
m.fs.rougher_mixer.make_up_state[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.rougher_mixer.recycle_state[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[m.fs.rougher_mixer.mixed_state[0].conc_mol_comp[component]] = (
1e5
)
m.scaling_factor[m.fs.rougher_sep.mixed_state[0].conc_mol_comp[component]] = 1e5
m.scaling_factor[m.fs.rougher_sep.recycle_state[0].conc_mol_comp[component]] = (
1e5
)
m.scaling_factor[m.fs.rougher_sep.purge_state[0].conc_mol_comp[component]] = 1e5
m.scaling_factor[
m.fs.rougher_mixer.make_up_state[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.rougher_mixer.recycle_state[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[m.fs.rougher_mixer.mixed_state[0].conc_mol_comp[component]] = (
1e5
)
m.scaling_factor[
m.fs.sc_circuit_purge.properties[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.cleaner_mixer.make_up_state[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.cleaner_mixer.recycle_state[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[m.fs.cleaner_mixer.mixed_state[0].conc_mol_comp[component]] = (
1e5
)
m.scaling_factor[
m.fs.sc_circuit_purge.properties[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_cleaner_load.mscontactor.organic[0, 1].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_cleaner_load.mscontactor.organic[0, 2].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_cleaner_load.mscontactor.organic[0, 3].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_cleaner_load.mscontactor.organic_inlet_state[0].conc_mol_comp[
component
]
] = 1e5
m.scaling_factor[
m.fs.solex_cleaner_load.mscontactor.organic[0, 1].conc_mol_comp[component]
] = 1e5
m.scaling_factor[m.fs.leach.liquid_inlet_state[0].conc_mol_comp[component]] = (
1e5
)
m.scaling_factor[m.fs.cleaner_sep.mixed_state[0].conc_mol_comp[component]] = 1e5
m.scaling_factor[m.fs.cleaner_sep.recycle_state[0].conc_mol_comp[component]] = (
1e5
)
m.scaling_factor[m.fs.cleaner_sep.purge_state[0].conc_mol_comp[component]] = 1e5
m.scaling_factor[
m.fs.cleaner_org_make_up.properties[0].conc_mol_comp[component]
] = 1e5
m.scaling_factor[m.fs.cleaner_purge.properties[0].conc_mol_comp[component]] = (
1e5
)
m.scaling_factor[
m.fs.solex_cleaner_strip.mscontactor.organic[0, 1].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_cleaner_strip.mscontactor.organic[0, 2].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_cleaner_strip.mscontactor.organic[0, 3].conc_mol_comp[component]
] = 1e5
m.scaling_factor[
m.fs.solex_cleaner_strip.mscontactor.organic_inlet_state[0].conc_mol_comp[
component
]
] = 1e5
m.scaling_factor[m.fs.solex_cleaner_load.mscontactor.aqueous[0, 1].flow_vol] = 1e-2
m.scaling_factor[m.fs.solex_cleaner_load.mscontactor.organic[0, 1].flow_vol] = 1e-2
m.scaling_factor[m.fs.solex_cleaner_strip.mscontactor.aqueous[0, 1].flow_vol] = 1e-2
m.scaling_factor[m.fs.solex_cleaner_strip.mscontactor.aqueous[0, 2].flow_vol] = 1e-2
m.scaling_factor[m.fs.solex_cleaner_strip.mscontactor.aqueous[0, 3].flow_vol] = 1e-2
m.scaling_factor[
m.fs.solex_cleaner_strip.mscontactor.aqueous_inlet_state[0].flow_vol
] = 1e-2
m.scaling_factor[m.fs.solex_cleaner_strip.mscontactor.organic[0, 1].flow_vol] = 1e-2
m.scaling_factor[m.fs.sl_sep2.solid_state[0].temperature] = 1e-2
m.scaling_factor[m.fs.sl_sep2.liquid_inlet_state[0].flow_vol] = 1e-2
m.scaling_factor[m.fs.sl_sep2.split.recovered_state[0].flow_vol] = 1e-2
m.scaling_factor[m.fs.sl_sep2.split.retained_state[0].flow_vol] = 1e-2
m.scaling_factor[m.fs.precip_sep.mixed_state[0].flow_vol] = 1e-2
m.scaling_factor[m.fs.precip_sep.recycle_state[0].flow_vol] = 1e-2
m.scaling_factor[m.fs.precip_sep.purge_state[0].flow_vol] = 1e-2
m.scaling_factor[m.fs.precip_purge.properties[0].flow_vol] = 1e-2
m.scaling_factor[m.fs.precipitator.cv_precipitate[0].temperature] = 1e2
m.scaling_factor[m.fs.precipitator.cv_aqueous.properties_in[0].flow_vol] = 1e-2
m.scaling_factor[m.fs.precipitator.cv_aqueous.properties_out[0].flow_vol] = 1e-2
m.scaling_factor[m.fs.roaster.gas_in[0].flow_mol] = 1e-3
m.scaling_factor[m.fs.roaster.gas_in[0].flow_mol_phase["Vap"]] = 1e-3
m.scaling_factor[m.fs.roaster.gas_in[0].temperature] = 1e-2
m.scaling_factor[m.fs.roaster.gas_in[0].pressure] = 1e-5
m.scaling_factor[m.fs.roaster.gas_out[0].flow_mol_phase["Vap"]] = 1e-3
m.scaling_factor[m.fs.roaster.gas_out[0].flow_mol] = 1e-3
m.scaling_factor[m.fs.roaster.gas_out[0].temperature] = 1e-2
m.scaling_factor[m.fs.roaster.gas_out[0].pressure] = 1e-5
m.scaling_factor[m.fs.roaster.solid_in[0].temperature] = 1e-2
scaling = TransformationFactory("core.scale_model")
scaled_model = scaling.create_using(m, rename=False)
return scaled_model
Step 2.6 Set the operating conditions#
Now specify the inlet feed conditions into the flowsheet and fix any necessary unit model variables such that the degrees of freedom of the system are equal to zero.
eps = 1e-7 * units.mg / units.L
m.fs.leach_liquid_feed.flow_vol.fix(224.3 * units.L / units.hour)
m.fs.leach_liquid_feed.conc_mass_comp.fix(1e-10 * units.mg / units.L)
m.fs.leach_liquid_feed.conc_mass_comp[0, "H"].fix(2 * 0.05 * 1e3 * units.mg / units.L)
m.fs.leach_liquid_feed.conc_mass_comp[0, "HSO4"].fix(1e-8 * units.mg / units.L)
m.fs.leach_liquid_feed.conc_mass_comp[0, "SO4"].fix(0.05 * 96e3 * units.mg / units.L)
m.fs.leach_solid_feed.flow_mass.fix(22.68 * units.kg / units.hour)
m.fs.leach_solid_feed.mass_frac_comp[0, "inerts"].fix(0.6952 * units.kg / units.kg)
m.fs.leach_solid_feed.mass_frac_comp[0, "Al2O3"].fix(0.237 * units.kg / units.kg)
m.fs.leach_solid_feed.mass_frac_comp[0, "Fe2O3"].fix(0.0642 * units.kg / units.kg)
m.fs.leach_solid_feed.mass_frac_comp[0, "CaO"].fix(3.31e-3 * units.kg / units.kg)
m.fs.leach_solid_feed.mass_frac_comp[0, "Sc2O3"].fix(2.77966e-05 * units.kg / units.kg)
m.fs.leach_solid_feed.mass_frac_comp[0, "Y2O3"].fix(3.28653e-05 * units.kg / units.kg)
m.fs.leach_solid_feed.mass_frac_comp[0, "La2O3"].fix(6.77769e-05 * units.kg / units.kg)
m.fs.leach_solid_feed.mass_frac_comp[0, "Ce2O3"].fix(0.000156161 * units.kg / units.kg)
m.fs.leach_solid_feed.mass_frac_comp[0, "Pr2O3"].fix(1.71438e-05 * units.kg / units.kg)
m.fs.leach_solid_feed.mass_frac_comp[0, "Nd2O3"].fix(6.76618e-05 * units.kg / units.kg)
m.fs.leach_solid_feed.mass_frac_comp[0, "Sm2O3"].fix(1.47926e-05 * units.kg / units.kg)
m.fs.leach_solid_feed.mass_frac_comp[0, "Gd2O3"].fix(1.0405e-05 * units.kg / units.kg)
m.fs.leach_solid_feed.mass_frac_comp[0, "Dy2O3"].fix(7.54827e-06 * units.kg / units.kg)
m.fs.leach.volume.fix(100 * units.gallon)
m.fs.load_sep.split_fraction[:, "recycle"].fix(0.9)
m.fs.scrub_sep.split_fraction[:, "recycle"].fix(0.9)
# Note: This stream + m.fs.s09 = 62.01 L/hr
m.fs.rougher_org_make_up.flow_vol.fix(6.201)
m.fs.rougher_org_make_up.conc_mass_comp[0, "Al"].fix(eps)
m.fs.rougher_org_make_up.conc_mass_comp[0, "Ca"].fix(eps)
m.fs.rougher_org_make_up.conc_mass_comp[0, "Fe"].fix(eps)
m.fs.rougher_org_make_up.conc_mass_comp[0, "Sc"].fix(eps)
m.fs.rougher_org_make_up.conc_mass_comp[0, "Y"].fix(eps)
m.fs.rougher_org_make_up.conc_mass_comp[0, "La"].fix(eps)
m.fs.rougher_org_make_up.conc_mass_comp[0, "Ce"].fix(eps)
m.fs.rougher_org_make_up.conc_mass_comp[0, "Pr"].fix(eps)
m.fs.rougher_org_make_up.conc_mass_comp[0, "Nd"].fix(eps)
m.fs.rougher_org_make_up.conc_mass_comp[0, "Sm"].fix(eps)
m.fs.rougher_org_make_up.conc_mass_comp[0, "Gd"].fix(eps)
m.fs.rougher_org_make_up.conc_mass_comp[0, "Dy"].fix(eps)
m.fs.acid_feed1.flow_vol.fix(0.09)
m.fs.acid_feed1.conc_mass_comp[0, "H2O"].fix(1000000)
m.fs.acid_feed1.conc_mass_comp[0, "H"].fix(10.36)
m.fs.acid_feed1.conc_mass_comp[0, "SO4"].fix(eps)
m.fs.acid_feed1.conc_mass_comp[0, "HSO4"].fix(eps)
m.fs.acid_feed1.conc_mass_comp[0, "Cl"].fix(359.64)
m.fs.acid_feed1.conc_mass_comp[0, "Al"].fix(eps)
m.fs.acid_feed1.conc_mass_comp[0, "Ca"].fix(eps)
m.fs.acid_feed1.conc_mass_comp[0, "Fe"].fix(eps)
m.fs.acid_feed1.conc_mass_comp[0, "Sc"].fix(eps)
m.fs.acid_feed1.conc_mass_comp[0, "Y"].fix(eps)
m.fs.acid_feed1.conc_mass_comp[0, "La"].fix(eps)
m.fs.acid_feed1.conc_mass_comp[0, "Ce"].fix(eps)
m.fs.acid_feed1.conc_mass_comp[0, "Pr"].fix(eps)
m.fs.acid_feed1.conc_mass_comp[0, "Nd"].fix(eps)
m.fs.acid_feed1.conc_mass_comp[0, "Sm"].fix(eps)
m.fs.acid_feed1.conc_mass_comp[0, "Gd"].fix(eps)
m.fs.acid_feed1.conc_mass_comp[0, "Dy"].fix(eps)
# TODO: flow rate and HCl concentration are not defined in REESim
m.fs.acid_feed2.flow_vol.fix(0.09)
m.fs.acid_feed2.conc_mass_comp[0, "H2O"].fix(1000000)
m.fs.acid_feed2.conc_mass_comp[0, "H"].fix(
10.36 * 4
) # Arbitrarily choose 4x the dilute solution
m.fs.acid_feed2.conc_mass_comp[0, "SO4"].fix(eps)
m.fs.acid_feed2.conc_mass_comp[0, "HSO4"].fix(eps)
m.fs.acid_feed2.conc_mass_comp[0, "Cl"].fix(359.64 * 4)
m.fs.acid_feed2.conc_mass_comp[0, "Al"].fix(eps)
m.fs.acid_feed2.conc_mass_comp[0, "Ca"].fix(eps)
m.fs.acid_feed2.conc_mass_comp[0, "Fe"].fix(eps)
m.fs.acid_feed2.conc_mass_comp[0, "Sc"].fix(eps)
m.fs.acid_feed2.conc_mass_comp[0, "Y"].fix(eps)
m.fs.acid_feed2.conc_mass_comp[0, "La"].fix(eps)
m.fs.acid_feed2.conc_mass_comp[0, "Ce"].fix(eps)
m.fs.acid_feed2.conc_mass_comp[0, "Pr"].fix(eps)
m.fs.acid_feed2.conc_mass_comp[0, "Nd"].fix(eps)
m.fs.acid_feed2.conc_mass_comp[0, "Sm"].fix(eps)
m.fs.acid_feed2.conc_mass_comp[0, "Gd"].fix(eps)
m.fs.acid_feed2.conc_mass_comp[0, "Dy"].fix(eps)
m.fs.rougher_sep.split_fraction[:, "recycle"].fix(0.9)
# TODO: flow rate and HCl concentration are not defined in REESim
m.fs.acid_feed3.flow_vol.fix(9)
m.fs.acid_feed3.conc_mass_comp[0, "H2O"].fix(1000000)
m.fs.acid_feed3.conc_mass_comp[0, "H"].fix(
10.36 * 4
) # Arbitrarily choose 4x the dilute solution
m.fs.acid_feed3.conc_mass_comp[0, "SO4"].fix(eps)
m.fs.acid_feed3.conc_mass_comp[0, "HSO4"].fix(eps)
m.fs.acid_feed3.conc_mass_comp[0, "Cl"].fix(359.64 * 4)
m.fs.acid_feed3.conc_mass_comp[0, "Al"].fix(eps)
m.fs.acid_feed3.conc_mass_comp[0, "Ca"].fix(eps)
m.fs.acid_feed3.conc_mass_comp[0, "Fe"].fix(eps)
m.fs.acid_feed3.conc_mass_comp[0, "Sc"].fix(eps)
m.fs.acid_feed3.conc_mass_comp[0, "Y"].fix(eps)
m.fs.acid_feed3.conc_mass_comp[0, "La"].fix(eps)
m.fs.acid_feed3.conc_mass_comp[0, "Ce"].fix(eps)
m.fs.acid_feed3.conc_mass_comp[0, "Pr"].fix(eps)
m.fs.acid_feed3.conc_mass_comp[0, "Nd"].fix(eps)
m.fs.acid_feed3.conc_mass_comp[0, "Sm"].fix(eps)
m.fs.acid_feed3.conc_mass_comp[0, "Gd"].fix(eps)
m.fs.acid_feed3.conc_mass_comp[0, "Dy"].fix(eps)
# Note: This stream + m.fs.s18 = 62.01 L/hr
m.fs.cleaner_org_make_up.flow_vol.fix(6.201)
m.fs.cleaner_org_make_up.conc_mass_comp[0, "Al"].fix(eps)
m.fs.cleaner_org_make_up.conc_mass_comp[0, "Ca"].fix(eps)
m.fs.cleaner_org_make_up.conc_mass_comp[0, "Fe"].fix(eps)
m.fs.cleaner_org_make_up.conc_mass_comp[0, "Sc"].fix(eps)
m.fs.cleaner_org_make_up.conc_mass_comp[0, "Y"].fix(eps)
m.fs.cleaner_org_make_up.conc_mass_comp[0, "La"].fix(eps)
m.fs.cleaner_org_make_up.conc_mass_comp[0, "Ce"].fix(eps)
m.fs.cleaner_org_make_up.conc_mass_comp[0, "Pr"].fix(eps)
m.fs.cleaner_org_make_up.conc_mass_comp[0, "Nd"].fix(eps)
m.fs.cleaner_org_make_up.conc_mass_comp[0, "Sm"].fix(eps)
m.fs.cleaner_org_make_up.conc_mass_comp[0, "Gd"].fix(eps)
m.fs.cleaner_org_make_up.conc_mass_comp[0, "Dy"].fix(eps)
m.fs.cleaner_sep.split_fraction[:, "recycle"].fix(0.9)
m.fs.sl_sep1.liquid_recovery.fix(0.7)
m.fs.sl_sep2.liquid_recovery.fix(0.88)
m.fs.precipitator.cv_precipitate[0].temperature.fix(348.15 * units.K)
m.fs.precip_sep.split_fraction[:, "recycle"].fix(0.9)
# Roaster gas feed
m.fs.roaster.deltaP.fix(0)
m.fs.roaster.gas_inlet.temperature.fix(1330)
m.fs.roaster.gas_inlet.pressure.fix(101325)
# Inlet flue gas mole flow rate
fgas = 0.00781
# Inlet flue gas composition, typical flue gas by burning CH4 with air with stoichiometric ratio of 2.3
gas_comp = {
"O2": 0.1118,
"H2O": 0.1005,
"CO2": 0.0431,
"N2": 0.7446,
}
for i, v in gas_comp.items():
m.fs.roaster.gas_inlet.mole_frac_comp[0, i].fix(v)
m.fs.roaster.gas_inlet.flow_mol.fix(fgas)
# Fix outlet product temperature
m.fs.roaster.gas_outlet.temperature.fix(873.15)
# Fix operating conditions
m.fs.roaster.frac_comp_recovery.fix(0.95)
Step 3: Solve the square problem#
Step 3.1: Initialize the system#
Since there are multiple recycle loops involved, sequential decomposition will be used to initialize the flowsheet and tear sets must be specified to successfully initialize the system.
def initialize_system(m):
# Initialize the model with sequential decomposition
seq = SequentialDecomposition()
seq.options.tear_method = "Direct"
seq.options.iterLim = 1
# Identify recycle streams
seq.options.tear_set = [
m.fs.leaching_feed_mixture,
m.fs.sx_rougher_load_aq_feed,
m.fs.sx_rougher_mixed_org_recycle,
m.fs.sx_cleaner_load_aq_feed,
m.fs.sx_cleaner_mixed_org_recycle,
]
# Print initialization order for user visibility
G = seq.create_graph(m)
order = seq.calculation_order(G)
print("Initialization Order")
for o in order:
print(o[0].name)
# Supply tear guesses with initial values that are close to the solution
tear_guesses1 = {
"flow_vol": {0: 747.99},
"conc_mass_comp": {
(0, "Al"): 180.84,
(0, "Ca"): 28.93,
(0, "Ce"): 5.48,
(0, "Dy"): 4.46e-11,
(0, "Fe"): 269.98,
(0, "Gd"): 2.60e-7,
(0, "H"): 20.06,
(0, "H2O"): 1000000,
(0, "HSO4"): 963.06,
(0, "Cl"): 1e-8,
(0, "La"): 0.0037,
(0, "Nd"): 1.81e-7,
(0, "Pr"): 3.65e-6,
(0, "SO4"): 486.24,
(0, "Sc"): 4.17e-11,
(0, "Sm"): 6.30e-10,
(0, "Y"): 7.18e-11,
},
}
tear_guesses2 = {
"flow_vol": {0: 62.01},
"conc_mass_comp": {
(0, "Al"): 1e-9,
(0, "Ca"): 1e-9,
(0, "Ce"): 1e-4,
(0, "Dy"): 1e-7,
(0, "Fe"): 1e-7,
(0, "Gd"): 1e-6,
(0, "La"): 1e-5,
(0, "Nd"): 1e-4,
(0, "Pr"): 1e-6,
(0, "Sc"): 250,
(0, "Sm"): 1e-6,
(0, "Y"): 1e-6,
},
}
tear_guesses3 = {
"flow_vol": {0: 520},
"conc_mass_comp": {
(0, "Al"): 430,
(0, "Ca"): 99,
(0, "Ce"): 2,
(0, "Dy"): 0.01,
(0, "Fe"): 660,
(0, "Gd"): 0.1,
(0, "H"): 2,
(0, "H2O"): 1000000,
(0, "HSO4"): 900,
(0, "Cl"): 0.1,
(0, "La"): 1,
(0, "Nd"): 1,
(0, "Pr"): 0.1,
(0, "SO4"): 4000,
(0, "Sc"): 0.05,
(0, "Sm"): 0.07,
(0, "Y"): 0.1,
},
}
tear_guesses4 = {
"flow_vol": {0: 64},
"conc_mass_comp": {
(0, "Al"): 1e-9,
(0, "Ca"): 1e-9,
(0, "Ce"): 1e-5,
(0, "Dy"): 1e-7,
(0, "Fe"): 1e-7,
(0, "Gd"): 1e-6,
(0, "La"): 1e-5,
(0, "Nd"): 1e-5,
(0, "Pr"): 1e-6,
(0, "Sc"): 321.34,
(0, "Sm"): 1e-6,
(0, "Y"): 1e-6,
},
}
tear_guesses5 = {
"flow_vol": {0: 5.7},
"conc_mass_comp": {
(0, "Al"): 5,
(0, "Ca"): 16,
(0, "Ce"): 346,
(0, "Dy"): 6,
(0, "Fe"): 1,
(0, "Gd"): 22,
(0, "H"): 14,
(0, "H2O"): 1000000,
(0, "HSO4"): 1e-7,
(0, "Cl"): 1400,
(0, "La"): 160,
(0, "Nd"): 121,
(0, "Pr"): 30,
(0, "SO4"): 1e-7,
(0, "Sc"): 149.2,
(0, "Sm"): 13,
(0, "Y"): 18,
},
}
# Pass the tear guesses to the sequential decomposition tool
seq.set_guesses_for(m.fs.leach.liquid_inlet, tear_guesses1)
seq.set_guesses_for(
m.fs.solex_rougher_load.mscontactor.organic_inlet, tear_guesses2
)
seq.set_guesses_for(
m.fs.solex_rougher_load.mscontactor.aqueous_inlet, tear_guesses3
)
seq.set_guesses_for(
m.fs.solex_cleaner_load.mscontactor.organic_inlet, tear_guesses4
)
seq.set_guesses_for(
m.fs.solex_cleaner_load.mscontactor.aqueous_inlet, tear_guesses5
)
# Associate units with specialized initializers
initializer_feed = FeedInitializer()
feed_units = [
m.fs.leach_liquid_feed,
m.fs.leach_solid_feed,
m.fs.rougher_org_make_up,
m.fs.acid_feed1,
m.fs.acid_feed2,
m.fs.acid_feed3,
m.fs.cleaner_org_make_up,
]
initializer_product = ProductInitializer()
product_units = [
m.fs.leach_filter_cake,
m.fs.leach_filter_cake_liquid,
m.fs.cleaner_purge,
m.fs.sc_circuit_purge,
m.fs.precip_purge,
]
initializer_sep = SeparatorInitializer()
sep_units = [
m.fs.scrub_sep,
m.fs.precip_sep,
m.fs.cleaner_sep,
m.fs.rougher_sep,
]
initializer_mix = MixerInitializer()
mix_units = [
m.fs.precip_sx_mixer,
m.fs.cleaner_mixer,
m.fs.rougher_mixer,
]
# The BT Initializer will be used for any units not handled by the above initializers
initializer_bt = BlockTriangularizationInitializer()
# Initialize units using their respective initializers
# For units that cannot be initialized with the initializers, the unit is manually fixed, solved, and then unfixed
def function(unit):
if unit in feed_units:
_log.info(f"Initializing {unit}")
initializer_feed.initialize(unit)
elif unit in product_units:
_log.info(f"Initializing {unit}")
initializer_product.initialize(unit)
elif unit in sep_units:
_log.info(f"Initializing {unit}")
initializer_sep.initialize(unit)
elif unit in mix_units:
_log.info(f"Initializing {unit}")
initializer_mix.initialize(unit)
elif unit == m.fs.leach:
_log.info(f"Initializing {unit}")
# Fix feed states
m.fs.leach.liquid_inlet.flow_vol.fix()
m.fs.leach.liquid_inlet.conc_mass_comp.fix()
m.fs.leach.solid_inlet.flow_mass.fix()
m.fs.leach.solid_inlet.mass_frac_comp.fix()
# Re-solve unit
solver = SolverFactory("ipopt")
solver.solve(m.fs.leach, tee=False)
# Unfix feed states
m.fs.leach.liquid_inlet.flow_vol.unfix()
m.fs.leach.liquid_inlet.conc_mass_comp.unfix()
m.fs.leach.solid_inlet.flow_mass.unfix()
m.fs.leach.solid_inlet.mass_frac_comp.unfix()
elif unit == m.fs.solex_rougher_load.mscontactor:
_log.info(f"Initializing {unit}")
# Fix feed states
m.fs.solex_rougher_load.mscontactor.organic_inlet_state[0].flow_vol.fix()
m.fs.solex_rougher_load.mscontactor.aqueous_inlet_state[0].flow_vol.fix()
m.fs.solex_rougher_load.mscontactor.organic_inlet_state[
0
].conc_mass_comp.fix()
m.fs.solex_rougher_load.mscontactor.aqueous_inlet_state[
0
].conc_mass_comp.fix()
# Re-solve unit
solver = SolverFactory("ipopt")
solver.solve(m.fs.solex_rougher_load, tee=False)
# Unfix feed states
m.fs.solex_rougher_load.mscontactor.organic_inlet_state[0].flow_vol.unfix()
m.fs.solex_rougher_load.mscontactor.aqueous_inlet_state[0].flow_vol.unfix()
m.fs.solex_rougher_load.mscontactor.organic_inlet_state[
0
].conc_mass_comp.unfix()
m.fs.solex_rougher_load.mscontactor.aqueous_inlet_state[
0
].conc_mass_comp.unfix()
elif unit == m.fs.solex_rougher_scrub.mscontactor:
_log.info(f"Initializing {unit}")
# Fix feed states
m.fs.solex_rougher_scrub.mscontactor.organic_inlet_state[0].flow_vol.fix()
m.fs.solex_rougher_scrub.mscontactor.aqueous_inlet_state[0].flow_vol.fix()
m.fs.solex_rougher_scrub.mscontactor.organic_inlet_state[
0
].conc_mass_comp.fix()
m.fs.solex_rougher_scrub.mscontactor.aqueous_inlet_state[
0
].conc_mass_comp.fix()
# Re-solve unit
solver = SolverFactory("ipopt")
solver.solve(m.fs.solex_rougher_scrub, tee=False)
# Unfix feed states
m.fs.solex_rougher_scrub.mscontactor.organic_inlet_state[0].flow_vol.unfix()
m.fs.solex_rougher_scrub.mscontactor.aqueous_inlet_state[0].flow_vol.unfix()
m.fs.solex_rougher_scrub.mscontactor.organic_inlet_state[
0
].conc_mass_comp.unfix()
m.fs.solex_rougher_scrub.mscontactor.aqueous_inlet_state[
0
].conc_mass_comp.unfix()
elif unit == m.fs.solex_rougher_strip.mscontactor:
_log.info(f"Initializing {unit}")
# Fix feed states
m.fs.solex_rougher_strip.mscontactor.organic_inlet_state[0].flow_vol.fix()
m.fs.solex_rougher_strip.mscontactor.aqueous_inlet_state[0].flow_vol.fix()
m.fs.solex_rougher_strip.mscontactor.organic_inlet_state[
0
].conc_mass_comp.fix()
m.fs.solex_rougher_strip.mscontactor.aqueous_inlet_state[
0
].conc_mass_comp.fix()
# Re-solve unit
solver = SolverFactory("ipopt")
solver.solve(m.fs.solex_rougher_strip, tee=False)
# Unfix feed states
m.fs.solex_rougher_strip.mscontactor.organic_inlet_state[0].flow_vol.unfix()
m.fs.solex_rougher_strip.mscontactor.aqueous_inlet_state[0].flow_vol.unfix()
m.fs.solex_rougher_strip.mscontactor.organic_inlet_state[
0
].conc_mass_comp.unfix()
m.fs.solex_rougher_strip.mscontactor.aqueous_inlet_state[
0
].conc_mass_comp.unfix()
elif unit == m.fs.solex_cleaner_load.mscontactor:
_log.info(f"Initializing {unit}")
# Fix feed states
m.fs.solex_cleaner_load.mscontactor.organic_inlet_state[0].flow_vol.fix()
m.fs.solex_cleaner_load.mscontactor.aqueous_inlet_state[0].flow_vol.fix()
m.fs.solex_cleaner_load.mscontactor.organic_inlet_state[
0
].conc_mass_comp.fix()
m.fs.solex_cleaner_load.mscontactor.aqueous_inlet_state[
0
].conc_mass_comp.fix()
# Re-solve unit
solver = SolverFactory("ipopt")
solver.solve(m.fs.solex_cleaner_load, tee=False)
# Unfix feed states
m.fs.solex_cleaner_load.mscontactor.organic_inlet_state[0].flow_vol.unfix()
m.fs.solex_cleaner_load.mscontactor.aqueous_inlet_state[0].flow_vol.unfix()
m.fs.solex_cleaner_load.mscontactor.organic_inlet_state[
0
].conc_mass_comp.unfix()
m.fs.solex_cleaner_load.mscontactor.aqueous_inlet_state[
0
].conc_mass_comp.unfix()
elif unit == m.fs.solex_cleaner_strip.mscontactor:
_log.info(f"Initializing {unit}")
# Fix feed states
m.fs.solex_cleaner_strip.mscontactor.organic_inlet_state[0].flow_vol.fix()
m.fs.solex_cleaner_strip.mscontactor.aqueous_inlet_state[0].flow_vol.fix()
m.fs.solex_cleaner_strip.mscontactor.organic_inlet_state[
0
].conc_mass_comp.fix()
m.fs.solex_cleaner_strip.mscontactor.aqueous_inlet_state[
0
].conc_mass_comp.fix()
# Re-solve unit
solver = SolverFactory("ipopt")
solver.solve(m.fs.solex_cleaner_strip, tee=False)
# Unfix feed states
m.fs.solex_cleaner_strip.mscontactor.organic_inlet_state[0].flow_vol.unfix()
m.fs.solex_cleaner_strip.mscontactor.aqueous_inlet_state[0].flow_vol.unfix()
m.fs.solex_cleaner_strip.mscontactor.organic_inlet_state[
0
].conc_mass_comp.unfix()
m.fs.solex_cleaner_strip.mscontactor.aqueous_inlet_state[
0
].conc_mass_comp.unfix()
else:
_log.info(f"Initializing {unit}")
initializer_bt.initialize(unit)
seq.run(m, function)
Step 3.2: Add solver#
Solve the model by running the flowsheet using the ipopt solver.
def solve(m):
solver = SolverFactory("ipopt")
results = solver.solve(m, tee=True)
Step 3.3 Solve the system#
Scale, initialize, and solve the model.
# Applies scaling to the model after the models are constructed and operating conditions are set
scaled_model = set_scaling(m)
# Initializes the scaled model
initialize_system(scaled_model)
# Solves the scaled model
solve(scaled_model)
Initialization Order
fs.leach_solid_feed
fs.leach
fs.sl_sep1
fs.leach_mixer
fs.sc_circuit_purge
fs.precip_sx_mixer
2024-08-26 15:11:44 [INFO] idaes.__main__: Initializing fs.leach_solid_feed
2024-08-26 15:11:44 [INFO] idaes.__main__: Initializing fs.leach_liquid_feed
2024-08-26 15:11:44 [INFO] idaes.__main__: Initializing fs.solex_rougher_load.mscontactor
2024-08-26 15:11:44 [INFO] idaes.__main__: Initializing fs.rougher_org_make_up
2024-08-26 15:11:44 [INFO] idaes.__main__: Initializing fs.acid_feed1
2024-08-26 15:11:45 [INFO] idaes.__main__: Initializing fs.acid_feed2
2024-08-26 15:11:45 [INFO] idaes.__main__: Initializing fs.solex_cleaner_load.mscontactor
WARNING: Loading a SolverResults object with a warning status into
model.name="fs.solex_cleaner_load";
- termination condition: infeasible
- message from solver: Ipopt 3.13.2\x3a Converged to a locally infeasible
point. Problem may be infeasible.
2024-08-26 15:11:45 [INFO] idaes.__main__: Initializing fs.cleaner_org_make_up
2024-08-26 15:11:45 [INFO] idaes.__main__: Initializing fs.acid_feed3
2024-08-26 15:11:45 [INFO] idaes.__main__: Initializing fs.leach
WARNING: Loading a SolverResults object with a warning status into
model.name="fs.leach";
- termination condition: infeasible
- message from solver: Ipopt 3.13.2\x3a Converged to a locally infeasible
point. Problem may be infeasible.
2024-08-26 15:11:45 [INFO] idaes.__main__: Initializing fs.load_sep
2024-08-26 15:11:45 [INFO] idaes.__main__: Initializing fs.solex_rougher_scrub.mscontactor
WARNING: Loading a SolverResults object with a warning status into
model.name="fs.solex_rougher_scrub";
- termination condition: infeasible
- message from solver: Ipopt 3.13.2\x3a Converged to a locally infeasible
point. Problem may be infeasible.
2024-08-26 15:11:45 [INFO] idaes.__main__: Initializing fs.solex_cleaner_strip.mscontactor
2024-08-26 15:11:45 [INFO] idaes.__main__: Initializing fs.sl_sep1
2024-08-26 15:11:46 [INFO] idaes.__main__: Initializing fs.scrub_sep
2024-08-26 15:11:46 [INFO] idaes.init.fs.scrub_sep: Initialization Step 2 Complete: optimal - Optimal Solution Found
2024-08-26 15:11:46 [INFO] idaes.__main__: Initializing fs.solex_rougher_strip.mscontactor
WARNING: Loading a SolverResults object with a warning status into
model.name="fs.solex_rougher_strip";
- termination condition: infeasible
- message from solver: Ipopt 3.13.2\x3a Converged to a locally infeasible
point. Problem may be infeasible.
2024-08-26 15:11:46 [INFO] idaes.__main__: Initializing fs.cleaner_sep
2024-08-26 15:11:46 [INFO] idaes.init.fs.cleaner_sep: Initialization Step 2 Complete: optimal - Optimal Solution Found
2024-08-26 15:11:46 [INFO] idaes.__main__: Initializing fs.precipitator
2024-08-26 15:11:47 [INFO] idaes.__main__: Initializing fs.leach_mixer
2024-08-26 15:11:47 [INFO] idaes.__main__: Initializing fs.leach_filter_cake
2024-08-26 15:11:47 [INFO] idaes.__main__: Initializing fs.leach_filter_cake_liquid
2024-08-26 15:11:47 [INFO] idaes.__main__: Initializing fs.leach_sx_mixer
2024-08-26 15:11:47 [INFO] idaes.__main__: Initializing fs.rougher_sep
2024-08-26 15:11:47 [INFO] idaes.init.fs.rougher_sep: Initialization Step 2 Complete: optimal - Optimal Solution Found
2024-08-26 15:11:47 [INFO] idaes.__main__: Initializing fs.cleaner_mixer
2024-08-26 15:11:47 [INFO] idaes.init.fs.cleaner_mixer: Initialization Complete: optimal - Optimal Solution Found
2024-08-26 15:11:47 [INFO] idaes.__main__: Initializing fs.cleaner_purge
2024-08-26 15:11:47 [INFO] idaes.__main__: Initializing fs.sl_sep2
2024-08-26 15:11:47 [INFO] idaes.__main__: Initializing fs.sc_circuit_purge
2024-08-26 15:11:47 [INFO] idaes.__main__: Initializing fs.rougher_mixer
2024-08-26 15:11:48 [INFO] idaes.init.fs.rougher_mixer: Initialization Complete: optimal - Optimal Solution Found
2024-08-26 15:11:48 [INFO] idaes.__main__: Initializing fs.roaster
2024-08-26 15:11:48 [INFO] idaes.__main__: Initializing fs.precip_sep
2024-08-26 15:11:48 [INFO] idaes.init.fs.precip_sep: Initialization Step 2 Complete: optimal - Optimal Solution Found
2024-08-26 15:11:48 [INFO] idaes.__main__: Initializing fs.precip_sx_mixer
2024-08-26 15:11:48 [INFO] idaes.init.fs.precip_sx_mixer: Initialization Complete: optimal - Optimal Solution Found
2024-08-26 15:11:48 [INFO] idaes.__main__: Initializing fs.precip_purge
2024-08-26 15:11:48 [INFO] idaes.__main__: Initializing fs.leach_solid_feed
2024-08-26 15:11:48 [INFO] idaes.__main__: Initializing fs.leach_liquid_feed
2024-08-26 15:11:48 [INFO] idaes.__main__: Initializing fs.rougher_org_make_up
2024-08-26 15:11:48 [INFO] idaes.__main__: Initializing fs.acid_feed1
2024-08-26 15:11:48 [INFO] idaes.__main__: Initializing fs.acid_feed2
2024-08-26 15:11:48 [INFO] idaes.__main__: Initializing fs.cleaner_org_make_up
2024-08-26 15:11:48 [INFO] idaes.__main__: Initializing fs.acid_feed3
2024-08-26 15:11:48 [INFO] idaes.__main__: Initializing fs.solex_cleaner_load.mscontactor
2024-08-26 15:11:48 [INFO] idaes.__main__: Initializing fs.solex_rougher_load.mscontactor
2024-08-26 15:11:48 [INFO] idaes.__main__: Initializing fs.leach
2024-08-26 15:11:48 [INFO] idaes.__main__: Initializing fs.solex_cleaner_strip.mscontactor
2024-08-26 15:11:49 [INFO] idaes.__main__: Initializing fs.solex_rougher_scrub.mscontactor
2024-08-26 15:11:49 [INFO] idaes.__main__: Initializing fs.load_sep
2024-08-26 15:11:49 [INFO] idaes.__main__: Initializing fs.sl_sep1
2024-08-26 15:11:49 [INFO] idaes.__main__: Initializing fs.leach_sx_mixer
2024-08-26 15:11:49 [INFO] idaes.__main__: Initializing fs.precipitator
2024-08-26 15:11:49 [INFO] idaes.__main__: Initializing fs.cleaner_sep
2024-08-26 15:11:49 [INFO] idaes.init.fs.cleaner_sep: Initialization Step 2 Complete: optimal - Optimal Solution Found
2024-08-26 15:11:49 [INFO] idaes.__main__: Initializing fs.solex_rougher_strip.mscontactor
2024-08-26 15:11:49 [INFO] idaes.__main__: Initializing fs.scrub_sep
2024-08-26 15:11:50 [INFO] idaes.init.fs.scrub_sep: Initialization Step 2 Complete: optimal - Optimal Solution Found
2024-08-26 15:11:50 [INFO] idaes.__main__: Initializing fs.sl_sep2
2024-08-26 15:11:50 [INFO] idaes.__main__: Initializing fs.cleaner_mixer
2024-08-26 15:11:50 [INFO] idaes.init.fs.cleaner_mixer: Initialization Complete: optimal - Optimal Solution Found
2024-08-26 15:11:50 [INFO] idaes.__main__: Initializing fs.rougher_sep
2024-08-26 15:11:50 [INFO] idaes.init.fs.rougher_sep: Initialization Step 2 Complete: optimal - Optimal Solution Found
2024-08-26 15:11:50 [INFO] idaes.__main__: Initializing fs.leach_mixer
2024-08-26 15:11:50 [INFO] idaes.__main__: Initializing fs.precip_sep
2024-08-26 15:11:50 [INFO] idaes.init.fs.precip_sep: Initialization Step 2 Complete: optimal - Optimal Solution Found
2024-08-26 15:11:50 [INFO] idaes.__main__: Initializing fs.rougher_mixer
2024-08-26 15:11:50 [INFO] idaes.init.fs.rougher_mixer: Initialization Complete: optimal - Optimal Solution Found
2024-08-26 15:11:50 [INFO] idaes.__main__: Initializing fs.precip_sx_mixer
2024-08-26 15:11:51 [INFO] idaes.init.fs.precip_sx_mixer: Initialization Complete: optimal - Optimal Solution Found
WARNING: Direct failed to converge in 1 iterations
2024-08-26 15:11:51 [INFO] idaes.__main__: Initializing fs.leach_filter_cake
2024-08-26 15:11:51 [INFO] idaes.__main__: Initializing fs.leach_filter_cake_liquid
2024-08-26 15:11:51 [INFO] idaes.__main__: Initializing fs.sc_circuit_purge
2024-08-26 15:11:51 [INFO] idaes.__main__: Initializing fs.cleaner_purge
2024-08-26 15:11:51 [INFO] idaes.__main__: Initializing fs.roaster
2024-08-26 15:11:51 [INFO] idaes.__main__: Initializing fs.precip_purge
Ipopt 3.13.2:
******************************************************************************
This program contains Ipopt, a library for large-scale nonlinear optimization.
Ipopt is released as open source code under the Eclipse Public License (EPL).
For more information visit http://projects.coin-or.org/Ipopt
This version of Ipopt was compiled from source code available at
https://github.com/IDAES/Ipopt as part of the Institute for the Design of
Advanced Energy Systems Process Systems Engineering Framework (IDAES PSE
Framework) Copyright (c) 2018-2019. See https://github.com/IDAES/idaes-pse.
This version of Ipopt was compiled using HSL, a collection of Fortran codes
for large-scale scientific computation. All technical papers, sales and
publicity material resulting from use of the HSL codes within IPOPT must
contain the following acknowledgement:
HSL, a collection of Fortran codes for large-scale scientific
computation. See http://www.hsl.rl.ac.uk.
******************************************************************************
This is Ipopt version 3.13.2, running with linear solver ma27.
Number of nonzeros in equality constraint Jacobian...: 9784
Number of nonzeros in inequality constraint Jacobian.: 0
Number of nonzeros in Lagrangian Hessian.............: 1535
Total number of variables............................: 3697
variables with only lower bounds: 2796
variables with lower and upper bounds: 16
variables with only upper bounds: 78
Total number of equality constraints.................: 3697
Total number of inequality constraints...............: 0
inequality constraints with only lower bounds: 0
inequality constraints with lower and upper bounds: 0
inequality constraints with only upper bounds: 0
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
0 0.0000000e+00 1.58e+06 1.00e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0
1 0.0000000e+00 5.65e+05 3.62e+07 -1.0 8.18e+05 - 7.43e-05 6.42e-01h 1
2 0.0000000e+00 2.03e+04 5.33e+06 -1.0 1.27e+05 - 2.26e-02 9.64e-01h 1
3 0.0000000e+00 2.05e+02 3.16e+05 -1.0 2.42e+04 - 8.10e-01 9.90e-01h 1
4 0.0000000e+00 1.90e+00 2.28e+03 -1.0 2.58e+02 - 9.89e-01 9.92e-01h 1
5 0.0000000e+00 9.48e-05 4.81e+04 -1.0 2.15e+00 - 9.90e-01 1.00e+00h 1
Number of Iterations....: 5
(scaled) (unscaled)
Objective...............: 0.0000000000000000e+00 0.0000000000000000e+00
Dual infeasibility......: 0.0000000000000000e+00 0.0000000000000000e+00
Constraint violation....: 2.5574220220920055e-10 9.4795362088007109e-05
Complementarity.........: 0.0000000000000000e+00 0.0000000000000000e+00
Overall NLP error.......: 2.5574220220920055e-10 9.4795362088007109e-05
Number of objective function evaluations = 6
Number of objective gradient evaluations = 6
Number of equality constraint evaluations = 6
Number of inequality constraint evaluations = 0
Number of equality constraint Jacobian evaluations = 6
Number of inequality constraint Jacobian evaluations = 0
Number of Lagrangian Hessian evaluations = 5
Total CPU secs in IPOPT (w/o function evaluations) = 0.074
Total CPU secs in NLP function evaluations = 0.004
EXIT: Optimal Solution Found.