PanLink SDK Examples

Comprehensive examples demonstrating PanLink integration for various simulation scenarios.

Table of Contents

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_property calls 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_properties to 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
        }
    }