MatCalc python integration

MatCalc comes with monaco-editor shipped and ready to create python scripts with code-completion in the Matcalc GUI.

The currently used python version is 3.10, so make sure to match this version should you decide to not use the shipped python version to run your scripts.

Running python scripts with MatCalc

Using python shipped with MatCalc

Easiest way to get started outside of the MatCalc GUI is by using the shipped python installation.

Windows PowerShell

$env:MATCALCPATH = "C:\Program Files\MatCalc 6.11.0.054"

& "$env:MATCALCPATH\python\python.exe" "$env:MATCALCPATH\scripts\script_menu\Python\Precipitation\al-base\example.py"

Windows command prompt

set MATCALCPATH=C:\Program Files\MatCalc 6.11.0.054

"%MATCALCPATH%\python\python.exe" "%MATCALCPATH%\scripts\script_menu\Python\Precipitation\al-base\example.py"

Unix

export MATCALCPATH="$HOME/MatCalc 6.11.0.054"

"$MATCALCPATH/python/bin/python3" "$MATCALCPATH/scripts/script_menu/Python/Precipitation/al-base/example.py"

Using external python (must be version 3.10)

Windows PowerShell

$env:MATCALCPATH = "C:\Program Files\MatCalc 6.11.0.054"
$env:PYTHONHOME = "$env:MATCALCPATH\python"

& python3.10.exe "$env:MATCALCPATH\scripts\script_menu\Python\Precipitation\al-base\example.py"

Windows command prompt

set MATCALCPATH=C:\Program Files\MatCalc 6.11.0.054
set PYTHONHOME=%MATCALCPATH%\python

python3.10.exe "%MATCALCPATH%\scripts\script_menu\Python\Precipitation\al-base\example.py"

Unix

export MATCALCPATH="$HOME/MatCalc 6.11.0.054"
export PYTHONHOME="$MATCALCPATH/python/lib"

python3.10 "$MATCALCPATH/scripts/script_menu/Python/Precipitation/al-base/example.py"

Developing with matcalc module

Initializing MatCalc core

import matcalc

mc = matcalc.get()
mc.init()

Using MatCalc script language

At the moment only some functions are exposed in the matcalc package but you can execute any MatCalc script language command.

mc.console("show expression T$c")

Working with databases

# thermodynamic database

mc.open_database("mc_al.tdb", matcalc.DatabaseType.EQUILIBRIUM)
mc.select_elements(["Al", "Mg", "Si", "Va"])
mc.select_phases(["FCC_A1", "CL_MGX", "mg5si6_b_dp"])
mc.read_database(matcalc.DatabaseType.EQUILIBRIUM)

# diffusion database

mc.open_database("mc_al.ddb", matcalc.DatabaseType.DIFFUSION)
mc.read_database(matcalc.DatabaseType.DIFFUSION)

print("databases: ")
print(f"  thermodynamic: {mc.database_name(matcalc.DatabaseType.EQUILIBRIUM)}")
print(f"  diffusion:     {mc.database_name(matcalc.DatabaseType.DIFFUSION)}")
print(f"  physical:      {mc.database_name(matcalc.DatabaseType.PHYSICAL)}")
print("")
print("selected elements:")
print(mc.elements())
print("")
print("selected phases:")
print(mc.phases())
print("")

Element Composition

# weight-percent

mc.setup_composition(
    matcalc.CompositionType.CT_WEIGHT_PERCENT,
    [
        matcalc.Composition("Mg", 0.7),
        matcalc.Composition("Si", 0.4)
    ])

# mole-fraction

mc.setup_composition(
    matcalc.CompositionType.CT_MOLE_FRACTION,
    [
        matcalc.Composition("Mg", 0.7),
        matcalc.Composition("Si", 0.4)
    ])

# u-fraction

mc.setup_composition(
    matcalc.CompositionType.CT_U_FRACTION,
    [
        matcalc.Composition("Mg", 0.7),
        matcalc.Composition("Si", 0.4)
    ])

Calculating equilibrium

mc.set_start_values()
mc.set_temperature_celsius(180.0)
mc.calculate_equilibrium(matcalc.OutputFlags.EO_NORMAL)

Precipitation domains

mc.console("create-precipitation-domain new-domain-name=almatrix")

for parameter, value in {
    "thermodynamic-matrix-phase": "fcc_a1",
    "initial-grain-diameter":     5.e-05,
    "vacancy-evolution-model":    "FSAK-dynamics",
    # add more parameters here
}.items():
    mc.set_precipitation_parameter("almatrix", parameter, value)

print(mc.precipitation_domains())

Precipitate phases

mc.console("create-new-phase parent-phase=cl_mgx precipitate")

for parameter, value in {
    "diffuse-interface-energy-effect":                              True,
    "precipitate properties regular-solution-critical-temperature": 800,
    "special coalescence-type":                                     "binder-stauffer-dynamics",
    "special coalescence-factor":                                   30,
    # add more parameters here
}.items():
    mc.set_precipitation_parameter("CL_MGX_P0", parameter, value)

Heat treatments

mc.console("create-tm-treatment tm-treatment-name=aging")

# Quenching from 540°C to 25°C with cooling rate of 100 K/s

mc.console("""
    append-tmt-segment tm-treatment-name=aging
    edit-tmt-segment tm-treatment-name=aging tm-treatment-segment=. segment-start-temperature=540
    edit-tmt-segment tm-treatment-name=aging tm-treatment-segment=. precipitation-domain=almatrix
    edit-tmt-segment tm-treatment-name=aging tm-treatment-segment=. T_end+T_dot segment-end-temperature=25 temperature-gradient=-100
    edit-tmt-segment tm-treatment-name=aging tm-treatment-segment=. segment-comment=Quenching of solution annealed material
""")

Plotting (when run in MatCalc GUI)

if mc.is_mcg():

    mc.console("""
      new-gui-window type-id=p1
      set-gui-window-property window-id=. default-x-axis-title=Time [h]
      set-gui-window-property window-id=. default-x-axis-factor=1/60/60
    """)

    # Temperature plot
    mc.console("""
      set-plot-option plot-id=. series new buffer variable-name=t$c
      set-plot-option plot-id=. y-axis-title=Temperature [°C]
      set-plot-option plot-id=. grid major enable-x-axis=yes
      set-plot-option plot-id=. grid major enable-y-axis=yes
    """)

Plotting with matplotlib

from ctypes import CFUNCTYPE, c_int
import matcalc
import matplotlib.pyplot as plt


mc = matcalc.get()
mc.init()


y = [[], []]
x = [[], []]
ax = None
fig = None


@CFUNCTYPE(None, c_int)
def buffer_append(index: int):
    """ called when a line gets added to the buffer """

    y[0].append(mc.get_calc_variable("F_PREC$CL_MGX_P0"))
    y[1].append(mc.get_calc_variable("F_PREC$mg5si6_b_dp_P0"))
    x[0].append(mc.get_calc_variable("STEPVALUE"))
    x[1].append(mc.get_calc_variable("STEPVALUE"))

    ax.plot(x[0], y[0])
    ax.plot(x[1], y[1])
    fig.canvas.draw()
    fig.canvas.flush_events()


@CFUNCTYPE(None, c_int)
def buffer_changed(index: int):
    """ called when the buffer changes """
    pass


plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y)

mc.register_buffer_append_callback(buffer_append)
mc.register_buffer_changed_callback(buffer_changed)

plt.show(block=False)

# your stuff

plt.show(block=True)

Kinetic step simulation

mc.calculate_kinetic_step()

Buffer variables

mc.get_calc_variable("T$c")

mc.get_buffer_variables(["F_PREC$CL_MGX_P0", "F_PREC$mg5si6_b_dp_P0"])

User variables

You can add buffer variables to MatCalc to be evaluated and or plotted.

import matcalc
from ctypes import CFUNCTYPE, c_char_p, c_double, c_int, c_void_p

mc: matcalc.MatCalc = None
user_variable_cache = {}


@CFUNCTYPE(c_double, c_int, c_int, c_char_p)
def user_variable(buffer: int, line: int, variable: bytes):
    """ Called when a user variable is to be resolved.

    Args:
        buffer   (int):   The index of the current buffer.
        line     (int):   The index of the current line in the buffer.
        variable (bytes): The name of the user variable.

    Returns:
        float: The value of the user variable.
    """

    if variable == b"user1":

        # calculate current value of "user1" variable
        # depending on the passed parameters
        value: float = mc.get_calc_variable("T$c") * buffer * line * 2

        return value

    raise ValueError(f"unknown variable {variable}")


def setup_mc_callbacks():
    # mc is not set when loading a workspace
    global mc
    if not mc:
        mc = matcalc.get()

    mc.set_user_variable_callback(user_variable)


def main():
    global mc

    mc = matcalc.get()
    mc.init()

    # your stuff


# This block is strictly necessary for callbacks.
#
if __name__ == "__main__":
    setup_mc_callbacks()
    main()
elif __name__ == "__mc__":
    # setup callbacks when loading this script from a workspace
    setup_mc_callbacks()

Callbacks

You can set up callback functions which are called on specific events.

import matcalc
from ctypes import CFUNCTYPE, c_char_p, c_double, c_int, c_void_p

mc: matcalc.MatCalc = None


@CFUNCTYPE(None, c_void_p)
def calc_step(user_data):
    """ called before a calc step """
    pass


@CFUNCTYPE(c_double, c_double, c_double)
def next_delta_time_step(delta_t: float, new_delta_t: float):
    """ return a new delta_t if you dare """
    return new_delta_t


@CFUNCTYPE(None, c_int)
def buffer_append(index: int):
    """ called when a line gets added to the buffer """
    pass


@CFUNCTYPE(None, c_int)
def buffer_changed(index: int):
    """ called when the buffer changes """
    pass


def setup_mc_callbacks():
    """ sets up all the callbacks """

    # mc is not set when loading a workspace
    global mc
    if not mc:
        mc = matcalc.get()

    mc.register_calc_callback(calc_step, None)
    mc.set_next_delta_time_step_callback(next_delta_time_step)
    mc.register_buffer_append_callback(buffer_append)
    mc.register_buffer_changed_callback(buffer_changed)


def main():
    global mc

    mc = matcalc.get()
    mc.init()
    # your stuff


# This block is strictly necessary for callbacks.
#
if __name__ == "__main__":
    setup_mc_callbacks()
    main()
elif __name__ == "__mc__":
    # setup callbacks when loading this script from a workspace
    setup_mc_callbacks()