Table of Contents
- 1. Single-Node PanEvolution Simulation
- 2. Multi-Node PanEvolution
- 3. Single-Node PanEvolution with Cooling Curve
- 4. Single-Node Precipitation Simulation
- 5. Single-Node PanSolidification Scheil
- 6. Multi-Element PanSolidification Scheil
- 7. Multi-Element PanPhaseEquilibrium Property Map
- 8. HTC Point Benchmark (Al-Mg-Zn)
1. Single-Node PanEvolution Simulation
This example demonstrates the fundamental usage of PanLink for a single-node material property simulation using the PanEvolution module. It covers the initialization of the library, setting up the database folder, and running a time-stepping loop to update material properties.
Key Features
- Basic PanLink initialization with
pl_reset. - Loading a material database using
pl_set_db_folder. - Retrieving system information (components, databases).
- Iterative time-stepping loop with fixed temperature.
- Accessing kinetic properties like grain size and dislocation density.
Key Code Snippets
// ----------------- PanLink Initialization -----------------
pl_reset(NULL, PanLink_Module_Type::PL_MODULE_TYPE_PanEvolution);
/// set DB folder: Specifies the directory containing the necessary database files.
char db_folder[128];
strcpy_s(db_folder, sizeof(db_folder), "./PanEvolution_DB_RX/"); // Database for recrystallization or similar
if (!pl_set_db_folder(db_folder, error_msg))
{
std::cout << "Error in pl_set_db_folder: " << error_msg << std::endl;
return 0;
}
// Main simulation loop
for (int i = 0; i < numSteps; i++)
{
//if (i >= 0) break; // Conditional break for debugging
double current_time = i * dt; // Calculate current simulation time
final_time = current_time; // Update final time reached
// Define state variables for the current time step
double strain = 0;
double strain_rate = 1;
variables.m_time = current_time;
variables.m_dt = dt;
variables.m_T = T; // Fixed temperature for this simulation
variables.m_strain = strain + strain_rate * current_time; // Accumulated strain
variables.m_strainRate = strain_rate; // Current strain rate
cell_id = ci; // Simple cell ID for this case
// Update material property using PanLink
// NULL for thread_id indicates single-threaded context for this call.
if (!pl_update_material_property(NULL, &cell_id, &variables, &outputs, error_msg))
{
cout << error_msg << endl; // Print error message if update fails
// Consider how to handle errors, e.g., break or return
pl_free_PanLink(); // Clean up before exiting on error
return 0; // Indicate failure
}
// Access results
// Grain size in microns - check if m_kinetic pointer is valid
if (outputs.m_kinetic) {
cout << outputs.m_kinetic->m_grain_Size * 1e6 << "\t";
} else {
cout << "N/A\t";
}
// Mean dislocation density - check if m_kinetic pointer is valid
if (outputs.m_kinetic) {
cout << outputs.m_kinetic->m_dislocation_Density_Mean << "\t";
} else {
cout << "N/A\t";
}
}
2. Multi-Node PanEvolution
This example showcases a multi-node simulation. PanLink can be used with multiple threads but the user wont gain significant improvement in speed. . It also includes a custom function to visualize results as an EPS map.
Key Features
- Multi-node simulation.
- Thread-safe
pl_update_material_propertycalls with thread ID can be used. - 2D grid simulation (multi-node).
- Visualization of results (e.g., grain size) using a color-mapped EPS file.
Key Code Snippets
// Main simulation loop (time stepping)
for (int i = 0; i < numSteps_sim; i++)
{
//if (i > 0) break; // Conditional break for debugging
// Set flag to collect properties on the last time step
if (i == numSteps_sim - 1) bCollectFinalProperties = true;
double current_time = i * dt_sim;
final_time_reached = current_time;
// Common state variables for this time step (some will be overridden per cell)
double base_strain = 0;
double base_strain_rate = 1; // This is overridden per cell later
variables_cell_input.m_time = current_time;
variables_cell_input.m_dt = dt_sim;
for (int ci = 0; ci < numCells_sim; ci++)
{
// Per-cell modifications to state variables
// Linearly interpolate temperature across cells
double current_cell_T = minT_sim + (maxT_sim - minT_sim) * static_cast(ci) / numCells_sim;
variables_cell_input.m_T = current_cell_T;
double cell_strain = 0; // Initial strain for this cell at t=0
// Linearly interpolate strain rate across cells
double cell_strain_rate = 0.01 + 0.5 * static_cast(ci) / numCells_sim;
variables_cell_input.m_strain = cell_strain + cell_strain_rate * current_time; // Accumulated strain for this cell
variables_cell_input.m_strainRate = cell_strain_rate; // Current strain rate for this cell
/// set position (assuming a 2D grid for visualization)
int grid_dim = static_cast(sqrt(static_cast(numCells_sim)));
if (grid_dim == 0) grid_dim = 1; // Avoid division by zero if numCells_sim is 0 or 1
variables_cell_input.m_position[0] = static_cast(ci % grid_dim);
variables_cell_input.m_position[1] = static_cast(ci / grid_dim);
variables_cell_input.m_position[2] = 0.0; // Z-position if needed
cell_id_loop = ci; // Using cell index as cell_id
// Call PanLink to update material property for the current cell and thread
// Note: error_msg is shared, but pl_update_material_property might be designed
// to only write to it on error, and then the first error wins.
// For robust error handling in parallel, each thread might need its own error buffer.
if (!pl_update_material_property(nullptr, &cell_id_loop, &variables_cell_input, &outputs_cell_result, error_msg))
{
// Decide on error handling: continue, break specific thread, or signal global stop.
// For now, it just prints and continues.
}
// If it's the last time step, store the results for EPS printing
if (bCollectFinalProperties)
{
// These assignments are safe because each thread writes to a unique 'ci' index.
vecFinalVars[ci] = variables_cell_input;
vecFinalProps[ci] = outputs_cell_result;
}
} // End of parallel loop over cells
// End line for tabular output (if bOutput is true)
if (bOutput)
{
if (i % out_step_sim == 0)
{
cout << endl;
}
}
if (i % (numSteps_sim / 10) == 0) {
cout << "current_time = " << current_time << " done ... " << endl;
cout.flush();
}
} // End of time stepping loop
3. Single-Node PanEvolution with Cooling Curve
This example simulates a material undergoing a specific thermal history defined by a cooling curve. It reads time-temperature pairs from an external file and interpolates the temperature for each simulation time step.
Key Features
- Reading external data (cooling curve) from a file.
- Linear interpolation of temperature based on current simulation time.
- Dynamic temperature update in the simulation loop.
Key Code Snippets
// Function to interpolate temperature
double interpolate_temperature(double time, const std::vector<std::pair<double, double>>& curve) {
// ... (Binary search and linear interpolation logic) ...
}
// ... In Main Loop ...
double T = interpolate_temperature(current_time, cooling_curve);
PanLink_State_Variable variables;
variables.m_time = current_time;
variables.m_dt = dt;
variables.m_T = T; // Dynamic T
pl_update_material_property(NULL, &cell_id, &variables, &outputs, error_msg);
4. Single-Node Precipitation Simulation
This example focuses on simulating precipitation kinetics alongside grain growth. It demonstrates how to access phase-specific properties such as volume fraction and average particle size for precipitate phases.
Key Features
- Simulation of concurrent grain growth and precipitation.
- Accessing
m_phase_propertiesto iterate through phases. - Retrieving phase fraction (
m_f) and average size (m_average_size).
Key Code Snippets
// ... In Simulation Loop ...
PanLink_State_Variable variables;
variables.m_T = 800.0; // Fixed Temperature
// ... Setup other variables ...
// Update material property
pl_update_material_property(NULL, &cell_id, &variables, &outputs, error_msg);
// Iterate through all phases to find precipitates
for (int p = 0; p < outputs.m_num_phase_properties; ++p) {
std::string phase_name = outputs.m_phase_properties[p].m_phaseName;
// Check for specific precipitate phase (e.g., "L12_FCC")
if (phase_name.find("L12_FCC") != std::string::npos) {
double fv = outputs.m_phase_properties[p].m_thermodynamic->m_f;
double size = outputs.m_phase_properties[p].m_kinetic->m_average_size;
// ...
}
}
5. Single-Node PanSolidification
This example introduces the PanSolidification module, specifically for performing Solidification simulations. It calculates solidification path properties like solid fraction, latent heat, and phase formation under a defined cooling rate.
Key Features
- Initialization of
PL_MODULE_TYPE_PanSoldification. - Setting up solidification parameters: Cooling Rate and Thermal Gradient.
- Accessing solidification-specific properties (Solid Fraction
m_fs).
Key Code Snippets
// Initialize PanSolidification
pl_reset(NULL, PanLink_Module_Type::PL_MODULE_TYPE_PanSoldification);
char db_folder[128];
strcpy_s(db_folder, sizeof(db_folder), "./PanSolidification_Scheil/");
pl_set_db_folder(db_folder, error_msg);
// ... In Loop ...
double T = maxT - (maxT - minT) * (current_time / total_time);
double cooling_rate = (maxT - minT) / total_time;
double thermal_gradient = cooling_rate / solidification_rate;
PanLink_State_Variable variables;
variables.m_T = T;
variables.m_cooling_rate = cooling_rate;
variables.m_thermal_gradient = thermal_gradient;
pl_update_material_property(NULL, &cell_id, &variables, &outputs, error_msg);
// Access Solid Fraction
if (outputs.m_solidification && outputs.m_solidification->m_fs >= 0) {
cout << outputs.m_solidification->m_fs << "\t";
}
6. Multi-Element PanSolidification
This example extends the Scheil simulation to a multi-element 2D domain. Each element is assigned a different maximum temperature and cooling rate, allowing for the simulation of solidification across a component with varying thermal history.
Key Features
- Spatially varying cooling rates and maximum temperatures.
- Independent Scheil simulations for each element.
- VTK output for solidification properties.
Key Code Snippets
// ... Setup Element Properties ...
element_maxT[elem] = minT_range + (maxT_range - minT_range) * f;
element_cooling_rate[elem] = (element_maxT[elem] - minT) / total_time;
// ... In Loop ...
// Calculate local T based on local cooling rate
double T = element_maxT[elem] - element_cooling_rate[elem] * current_time;
if (T < minT) T = minT;
variables.m_T = T;
variables.m_cooling_rate = element_cooling_rate[elem];
pl_update_material_property(NULL, &cell_id, &variables, &outputs, error_msg);
7. Multi-Element PanPhaseEquilibrium Property Map
This example utilizes the PanPhaseEquilibrium module to calculate thermodynamic properties across a composition gradient. By mapping composition to grid coordinates, it effectively generates a property map (e.g., Gibbs Energy landscape) for a ternary system.
Key Features
- Initialization of
PL_MODULE_TYPE_PanPhaseEquilibrium. - Mapping grid coordinates to chemical composition (x1, x2).
- Calculation of equilibrium properties (G, H, S) for varying compositions.
Key Code Snippets
// Initialize PanPhaseEquilibrium
pl_reset(NULL, PanLink_Module_Type::PL_MODULE_TYPE_PanPhaseEquilibrium);
// ... In Loop ...
// Map element index to composition
double x1 = element_concentrations1[elem];
double x2 = element_concentrations2[elem];
PanLink_State_Variable variables;
variables.m_num_n = 3;
variables.m_n[1] = x1; // Component 1
variables.m_n[2] = x2; // Component 2
variables.m_n[0] = 1.0 - x1 - x2; // Balance
pl_update_material_property(NULL, &cell_id, &variables, &outputs, error_msg);
// Access Thermodynamic Data
if (outputs.m_thermodynamic) {
double G = outputs.m_thermodynamic->m_G;
}
8. HTC Point Benchmark (Al-Mg-Zn)
This example performs High-Throughput Calculations (HTC) for a ternary system (Al-Mg-Zn). It iterates through a dense grid of compositions to map out the phase equilibrium and thermodynamic properties across the entire composition space.
Key Features
- Nested loops for systematic composition variation.
- Massive number of point calculations (HTC).
- Detailed output of thermodynamic potentials (G, H, S) and phase constitution.
Key Code Snippets
// Nested loops for composition (Mg, Zn)
for (int iZn = x_Zn_start; iZn < numStepsMg; ++iZn) {
for (int iMg = x_Mg_start; iMg < numStepsZn; ++iMg)
{
// ... Calculate composition fractions ...
double xMg = ...;
double xZn = ...;
PanLink_State_Variable variables;
variables.m_T = 500; // Fixed T
variables.m_num_n = 3;
variables.m_n[1] = xMg;
variables.m_n[2] = xZn;
variables.m_n[0] = 1.0 - xMg - xZn; // Al Balance
// Calculate properties
pl_update_material_property(nullptr, &cell_id, &variables, &outputs, error_msg);
// Output Results
cout << outputs.m_thermodynamic->m_G << "\t";
cout << outputs.m_thermodynamic->m_mu[0] << "\t"; // Chemical potential
}
}