Architecture overview

TC-Toolbox contains classes of these types:

  • TCToolbox – this is where you start with general settings.

  • SystemBuilder and System – where you choose database and elements etc.

  • Calculation – where you choose and configure the calculation.

  • Result – where you get the results from a calculation you have run.

TCToolbox

This is the starting point for all TC-Toolbox usage.

You can think of this as the start of a “wizard”.

You use it to select databases and elements and then in the next step, configure the system.

Example:

import tc_toolbox.*

session = TCToolbox();
session.select_database_and_elements(...
% e.t.c.
% ...

Note

When your MATLAB® script runs a row like this:

session = TCToolbox();

a process running a calculation server starts. Your code, via TC-Toolbox, uses socket communication to send and receive messages to and from that server.

When you remove the variable session from the MATLAB® workspace, the calculation server automatically shuts down, and all temporary files are deleted.

Note

You can set up a folder location to re-use results from saved calculations. This folder can be a network folder and shared by many users. This is done using the method set_cache_folder().

import tc_toolbox.*

session = TCToolbox();
session.set_cache_folder("cache")

Once the cache folder is created, if a previous TC-Toolbox calculation has run with the same cache folder and exactly the same system and calculation settings, the calculation is not re-run. Instead the result is automatically loaded from disk.

It is also possible to explicitly save and load results.

Example:

import tc_toolbox.*

session = TCToolbox();
%... diffusion calculation (could be any calculation type)
calculation_result.save_to_disk('path to folder')
%...
loaded_result = start.load_result_from_disk().diffusion('path to folder')

SystemBuilder and System

A SystemBuilder is returned when you have selected your database and elements in TCToolbox.

The SystemBuilder lets you further specify your system, for example the phases that should be part of your system.

Example:

import tc_toolbox.*

session = TCToolbox();
start.select_database_and_elements("ALDEMO", ["Al", "Sc"]).select_phase("FCC_A1")
% e.t.c

When all configuration is done, you call get_system() which returns an instance of a System class. The System class is immutable and cannot be changed. If you want to change the database, elements, or something else, you can:

  • change the SystemBuilder and call get_system() again, or

  • create a new SystemBuilder and call get_system().

From the System you can create one or more calculations, which is the next step in the “wizard”.

Note

You can use the same System object to create several calculations.

Calculation

All available calculation types are set up in a similar way, some calculations have many settings. But default values are used where it is applicable, and are overridden if you specify something different.

Tip

Review the TC-Toolbox examples included with the Thermo-Calc installation to see how calculations are used for various solutions.

When you have configured your calculation you call calculate() to start the actual calculation. That returns a Result, which is the next step.

Single Equilibrium Calculations

In single equilibrium calculations you need to specify the correct number of conditions, depending on how many elements your System contains.

This is done by calling set_condition().

An important difference from other calculations is that single equilibrium calculations have two functions to get result values.

The calculate() method, which gives a SingleEquilibriumTempResult, is used to get actual values. This result is temporary, meaning that if you run other calculations or rerun the current one, the resulting object no longer gives values corresponding to the first calculation.

This is different from how other calculations work. If you want a Result that you can use after running other calculations, you need to call calculate_with_state(), which returns a SingleEquilibriumResult.

Note

calculate() is the recommended function and works in almost all situations. Also it has significantly better performance than calculate_with_state().

Example:

import tc_toolbox.*

session = TCToolbox();

sys = session.select_database_and_elements("FEDEMO", ["Fe", "Cr", "C"]).get_system();
calculation = sys.with_single_equilibrium_calculation()...
                 .set_condition(ThermodynamicQuantity.temperature(), 2000.0)...
                 .set_condition(ThermodynamicQuantity.mole_fraction_of_a_component("Cr"), 0.1)...
                 .set_condition(ThermodynamicQuantity.mole_fraction_of_a_component("C"), 0.01)...
                 .calculate();

gibbs_energy = calculation.get_value_of("G")

Batch Equilibrium Calculations

Batch equilibrium calculations are used when you want to do many single equilibrium calculations and it is known from the beginning which result values are required from the equilibrium. This is a vectorized type of calculation that can reduce the overhead from MATLAB® and TC-Toolbox.

Tip

The performance of batch equilibrium calculations can be significantly better than looping and using single equilibrium calculations if the actual Thermo-Calc calculation is fast. There is little advantage if the Thermo-Calc equilibrium calculations take a long time (typically for large systems and databases).

Example:

import tc_toolbox.*

session = TCToolbox();
session.set_cache_folder("_cache");

system_builder = session.select_database_and_elements("NIDEMO", ["Ni", "Al", "Cr"]);
system_builder.without_default_phases();
system_builder.select_phase('BCC_A2');
sys = system_builder.get_system();
batch_calculation = sys.with_batch_equilibrium_calculation();

batch_calculation.set_condition("T", 800);
batch_calculation.set_condition("X(Al)", 1E-2);
batch_calculation.set_condition("X(Cr)", 1E-2);
batch_calculation.disable_global_minimization();

list_of_x_Al = linspace(1e-4, 10e-2, 10);
list_of_x_Cr = linspace(1e-4, 15e-2, 10);
list_of_density = [];
equilibria = {};

i = 1;
for x_Al = list_of_x_Al
    for x_Cr = list_of_x_Cr
        equilibria{i} = {{"X(Al)", x_Al} {"X(Cr)", x_Cr}};
        i = i+1;
    end
end

batch_calculation.set_conditions_for_equilibria(equilibria);

results = batch_calculation.calculate(["BM", "VM"], 100);

masses = results.get_values_of("BM");
volumes = results.get_values_of('VM');
density = 1e-3 * masses ./ volumes

Precipitation Calculations

All the configuration settings for the Precipitation Calculator in Graphical Mode are available for this calculation. However, you must at least enter a matrix phase, a precipitate phase, temperature, simulation time, and compositions.

Example:

import tc_toolbox.precipitation.*
import tc_toolbox.*

session = TCToolbox();
session.set_cache_folder("_cache");

system_builder = session.select_thermodynamic_and_kinetic_databases_with_elements("ALDEMO", "MALDEMO", ["Al","Sc"]);
sys = system_builder.get_system();

precipitationCalculation = sys.with_isothermal_precipitation_calculation();
precipitationCalculation.set_composition("Sc", 0.18);
precipitationCalculation.set_temperature(623.15);
precipitationCalculation.set_simulation_time(1e5);
precipitationCalculation.with_matrix_phase(MatrixPhase("FCC_A1")...
                                           .add_precipitate_phase(PrecipitatePhase("AL3SC")));

result = precipitationCalculation.calculate();
[time, meanRadius] = result.get_mean_radius_of("AL3SC");

Scheil Calculations

All Scheil calculation settings available in Graphical Mode (using the Scheil Calculator) or Console Mode (using the Scheil module) are available for this calculation. The minimum you need to specify are the elements and compositions. Everything else is set to a default value.

Example:

import tc_toolbox.*

session = TCToolbox();
sys = session.select_database_and_elements("FEDEMO", ["Fe", "C"]).get_system();
temperature_vs_mole_fraction_of_solid = sys.with_scheil_calculation()...
                                           .set_composition("C", 0.3)...
                                           .calculate()...
                                           .get_values_of(ScheilQuantity.temperature(),...
                                                          ScheilQuantity.mole_fraction_of_all_solid_phases());

Property Diagram Calculations

For the property diagram (step) calculation, everything that you can configure in the Equilibrium Calculator when choosing One axis in Graphical Mode can also be configured in this calculation. In Console Mode the property diagram is created using the Step command. The minimum you need to specify are elements, conditions, and the calculation axis. All other settings use the default values unless specified otherwise.

Example:

import tc_toolbox.*
import tc_toolbox.step_or_map_diagrams.*

session = TCToolbox();
    property_diagram = session...
            .select_database_and_elements("FEDEMO", ["Fe", "C"])...
            .get_system()...
            .with_property_diagram_calculation()...
                .with_axis(CalculationAxis(ThermodynamicQuantity.temperature())...
                           .set_min(500)...
                           .set_max(3000))...
                .set_condition(ThermodynamicQuantity.mole_fraction_of_a_component("C"), 0.01)...
                .calculate()...
                .get_values_grouped_by_stable_phases_of(ThermodynamicQuantity.temperature(),...
                                                        ThermodynamicQuantity.volume_fraction_of_a_phase("ALL"));

Phase Diagram Calculations

For the phase diagram (map) calculation, everything that you can configure in the Equilibrium Calculator when choosing Phase diagram in Graphical Mode can also be configured in this calculation. In Console Mode the phase diagram is created using the Map command. The minimum you need to specify are elements, conditions, and two calculation axes. All other settings use the default values unless specified otherwise.

Example:

import tc_toolbox.*
import tc_toolbox.step_or_map_diagrams.*

session = TCToolbox();
    property_diagram = session...
            .select_database_and_elements("FEDEMO", ["Fe", "C"])...
            .get_system()...
            .with_phase_diagram_calculation()...
                .with_first_axis(CalculationAxis(ThermodynamicQuantity.temperature())...
                                 .set_min(500)...
                                 .set_max(3000))...
                .with_second_axis(CalculationAxis(ThermodynamicQuantity.mole_fraction_of_a_component("C"))...
                                  .set_min(0)...
                                  .set_max(1))...
                .set_condition(ThermodynamicQuantity.mole_fraction_of_a_component("C"), 0.01)...
                .calculate()...
                .get_values_grouped_by_stable_phases_of(ThermodynamicQuantity.mass_fraction_of_a_component("C"),...
                                                        ThermodynamicQuantity.temperature());

Diffusion Calculations

For diffusion calculations, everything that you can configure in the Diffusion Calculator can also be configured in this calculation. The minimum you need to specify are elements, temperature, simulation time, a region with a grid and width, a phase, and an initial composition.

Example:

import tc_toolbox.diffusion.*
import tc_toolbox.*

session = TCToolbox();

tc_system = session...
    .select_thermodynamic_and_kinetic_databases_with_elements("FEDEMO", "MFEDEMO", ["Fe", "Ni"])...
    .get_system();

calculator = tc_system...
                .with_isothermal_diffusion_calculation()...
                .set_temperature(1400.0)...
                .set_simulation_time(108000.0)...
                .add_region(Region("Austenite")...
                            .set_width(100E-6)...
                            .with_grid(CalculatedGrid.linear()...
                                       .set_no_of_points(50))...
                                       .with_composition_profile(CompositionProfile()...
                                                                 .add("Ni", ElementProfile.linear(10.0, 50.0)))...
                                       .add_phase("FCC_A1"));

results = calculator.calculate();

[distance, mass_frac_ni] = results.get_mass_fraction_of_component_at_time("Ni", SimulationTime.LAST);

Property Model Calculations

For Property Model calculations, all the configuration settings for the Property Model Calculator in Graphical Mode are available for this calculation. The minimum you need to specify are elements, composition, and which Property Model you want to use.

Example:

import tc_toolbox.*

session = TCToolbox();
"Available property models: " + session.get_property_models()

property_model = session...
                    .select_database_and_elements("FEDEMO", ["Fe", "C"])...
                    .get_system()...
                    .with_property_model_calculation("Driving Force")...
                    .set_composition("C", 1.0)...
                    .set_argument("matrix", "LIQUID")...
                    .set_argument("precipitate", "GRAPHITE");

"Available arguments: " + property_model.get_arguments()
result = property_model.calculate();

"Available result quantities: " + result.get_result_quantities()
driving_force = result.get_value_of("normalizedDrivingForce")

Material to Material Calculations

Material to Material calculations are generally regular single equilibrium, property diagram or phase diagram calculations but they are specialised to handle the mixture of two materials A and B. Everything that you can configure in the Material to Material Calculator in Graphical Mode can also be configured in this calculation. The minimum required configuration is shown below for a Property diagram calculation for varying amount of material B. The other calculators (single fraction of material B and phase diagram calculations) are configured in a similar way.

Example:

import tc_toolbox.*
import tc_toolbox.material_to_material.*;

independent_elements = ["Cr", "Ni"];
a_comp = [10.0, 15.0];
b_comp = [15.0, 10.0];

activity_elements = ["C"];
activities = [0.1];

session = TCToolbox();

material_to_material_property_diagram = session...
    .select_database_and_elements("FEDEMO", ["Fe", "Cr", "Ni", "C"])...
    .get_system()...
    .with_material_to_material()...
        .with_property_diagram_calculation()...
        .set_material_a(containers.Map(independent_elements, a_comp), "Fe")...
        .set_material_b(containers.Map(independent_elements, b_comp), "Fe")...
        .set_activities(containers.Map(activity_elements, activities))...
        .with_constant_condition(ConstantCondition.temperature(800 + 273.15))...
        .with_axis(MaterialToMaterialCalculationAxis.fraction_of_material_b());

result = material_to_material_property_diagram.calculate();
data = result.get_values_grouped_by_quantity_of(...
    Constants.MATERIAL_B_FRACTION,...
    ThermodynamicQuantity.volume_fraction_of_a_phase(Constants.ALL_PHASES));

for k = data.keys()
    group = data(k{1});
    fractions_of_b = group.get_x();
    volume_fraction_of_phase = group.get_y();
    phase_name = group.get_label();
end

Process Metallurgy Calculations

Process Metallurgy calculations are specialized to support the convenient handling of component-based additions (i.e., slag compositions such as 50% Al2O3 - 30% CaO - 20% SiO2), provide tailor-made result quantities, a framework for developing kinetic process simulations, and more useful features.

There are two distinct type of calculations:

  • tc_toolbox.process_metallurgy.equilibrium.EquilibriumCalculation: isothermal and adiabatic equilibrium calculations

  • tc_toolbox.process_metallurgy.process.ProcessSimulationCalculation: a kinetic process simulation framework, based an Effective Equilibrium Reaction Zone (EERZ) approach

Equilibrium calculation example:

Equilibrium calculations are useful in a large large of situations when considering the kinetics of a process is unnecessary.

import tc_toolbox.process_metallurgy.base.*;
import tc_toolbox.process_metallurgy.equilibrium.*;
import tc_toolbox.*

session = tc_toolbox.TCToolbox();

metal = EquilibriumAddition(containers.Map(["Fe", "C", "Si"], {NaN, 4.5, 1.0}), 100e3, 1650 + 273.15);
slag = EquilibriumAddition(containers.Map(["CaO", "Al2O3"], {75, 25}), 3e3, 1600 + 273.15);
gas = EquilibriumGasAddition(containers.Map({'O2'}, {100}), 1000, GasAmountUnit.NORM_CUBIC_METER);
calc = session.with_metallurgy().with_adiabatic_equilibrium_calculation(ProcessDatabase.OXDEMO);

(calc...
 .add_addition(metal)...
 .add_addition(slag)...
 .add_addition(gas));

result = calc.calculate();

disp("Stable phases:")
disp(result.get_stable_phases())
disp("Temperature: " + result.get_temperature() + " K");

Process simulation example:

TC-Toolbox is providing a framework for modelling in principle any process in metallurgy, especially steel-making. It is up to the user to actually develop a concrete model for the process in question. The framework is in the current release limited to one reaction zone connecting two bulk zones. These bulk zones are typically the steel melt and the top slag, but not limited to that. The framework in its current version has proven to be useful to model industrial ladle furnaces, AOD- and VOD-converters and more. Process features such as heating and cooling, heat transfer between the bulk zones, inclusion formation and their flotation, etc., can be modelled.

This is a very simplified minimal but complete model mimicking a BOF process:

import tc_toolbox.process_metallurgy.base.*;
import tc_toolbox.process_metallurgy.process.*;
import tc_toolbox.*

session = tc_toolbox.TCToolbox();


calc = (session.with_metallurgy()...
        .with_adiabatic_process_calculation(ProcessDatabase.OXDEMO)...
        .set_end_time(15 * 60));

steel_zone = MetalBulkZone(7800);
slag_zone = SlagBulkZone(4500);

steel_zone.add_addition(SingleTimeAddition(containers.Map(["Fe", "C", "Si"], {NaN, 4.5, 1.0}), 120e3,...
                                           1600 + 273.15), 0);
slag_zone.add_addition(SingleTimeAddition(containers.Map(["CaO", "SiO2"], {75, 25}), 1.2e3,...
                                          1500 + 273.15,...
                                          CompositionUnit.MOLE_PERCENT), 0);

steel_zone.add_continuous_addition(ContinuousGasAddition(containers.Map({'O2'}, {100}), 1,...
                                                         GasRateUnit.NORM_CUBIC_METER_PER_SEC));

calc.with_reaction_zone(ReactionZone(10.0,...
                                     steel_zone, 1.0e-5,...
                                     slag_zone, 1.0e-6));

result = calc.calculate();

disp("Stable phases in the steel melt:")
disp(result.get_stable_phases('metal'))
disp("C-content in steel vs. time:")
compositions = result.get_composition_of_phase_group('metal', PhaseGroup.ALL_METAL);
disp(compositions('C'))

Result

All calculations have a method called calculate() that starts the calculations and when finished, returns a Result.

The Result classes have very different methods, depending on the type of calculation.

The Result is used to get numerical values from a calculation that has run.

The Result can be saved to disk by the method save_to_disk().

Previously saved results can be loaded by the method load_result_from_disk() on the SetUp class.

Example:

% code above sets up the calculation
r = calculation.calculate()
time, meanRadius = r.get_mean_radius_of("AL3SC")

The Result objects are completely independent from calculations done before or after they are created. The objects return valid values corresponding to the calculation they were created from, for their lifetime. The only exception is if you call calculate() and not calculate_with_state() on a single equilibrium calculation.

As in the following example you can mix different calculations and results, and use old results after another calculation has run.

Example:

% ...
% some code to set up a single equilibrium calculation
% ...

single_eq_result = single_eq_calculation.calculate_with_state()

% ...
% some code to set up a precipitation calculation
% ...

prec_result = precipitation_calculation.calculate()

% ...
% some code to set up a Scheil calculation
% ...

scheil_result = scheil_calculations.calculate()

% now it is possible to get results from the single equilibrium calculation,
% without having to re-run it (because it has been calculated with saving of the state)

gibbs = single_eq_result.get_value_of("G")