# PhaseFieldX Logging Capabilities: A Complete Guide to Simulation Tracing and Reproducibility

> Explore PhaseFieldX logging capabilities for simulation tracing and reproducibility.  Capture system metadata, parameters, and timing for complete simulation tracking.

- Repository: [Miguel Castillón/phasefieldx](https://github.com/castillonmiguel/phasefieldx)
- Tags: how-to-guide
- Published: 2026-02-27

---

**PhaseFieldX provides comprehensive logging capabilities through Python's built-in `logging` module, creating a dedicated `simulation_logger` that writes structured execution traces to `simulation.log` in the results folder, capturing system metadata, library versions, input parameters, solver convergence, and timing information for full reproducibility.**

PhaseFieldX implements a structured logging system designed for scientific reproducibility and debugging. According to the source code in `castillonmiguel/phasefieldx`, every simulation run generates a persistent text record that captures the complete computational environment, configuration parameters, and runtime progress in a single, human-readable file.

## Centralized Logger Architecture

The logging system revolves around a single **simulation logger** named `simulation_logger` that is configured once per analysis and shared across all components.

### The `set_logger` Factory Function

The function **`set_logger`** in [[`src/phasefieldx/Logger/library_versions.py`](https://github.com/castillonmiguel/phasefieldx/blob/main/src/phasefieldx/Logger/library_versions.py)](https://github.com/castillonmiguel/phasefieldx/blob/main/src/phasefieldx/Logger/library_versions.py) creates and configures the logger:

```python
import logging
import os

def set_logger(result_folder_name):
    logger = logging.getLogger('simulation_logger')
    if logger.hasHandlers():
        for handler in logger.handlers:
            logger.removeHandler(handler)
    logger.setLevel(logging.INFO)
    
    simulation_file_handler = logging.FileHandler(
        os.path.join(result_folder_name, 'simulation.log'))
    simulation_formatter = logging.Formatter('%(message)s')
    simulation_file_handler.setFormatter(simulation_formatter)
    logger.addHandler(simulation_file_handler)
    
    return logger

```

**Key characteristics:**
- **Output location**: `<results_folder>/simulation.log`
- **Log level**: `INFO` (captures standard operation while allowing optional `DEBUG` granularity)
- **Format**: Plain `%(message)s` without timestamps, since the code adds its own temporal metadata
- **Singleton pattern**: Removes existing handlers to prevent duplicate log entries in repeated runs

## System and Library Metadata Capture

Immediately after initialization, PhaseFieldX records three categories of environmental metadata to ensure reproducibility across different machines and software versions.

### Environment Information with `log_system_info`

The **`log_system_info`** function (lines 91–115 in [[`library_versions.py`](https://github.com/castillonmiguel/phasefieldx/blob/main/library_versions.py)](https://github.com/castillonmiguel/phasefieldx/blob/main/src/phasefieldx/Logger/library_versions.py#L91-L115)) logs:

- Operating system and architecture
- User name and machine type
- Processor details
- Python version

### Dependency Version Tracking with `log_library_versions`

The **`log_library_versions`** function (lines 62–88) records critical library versions for dependency verification:

- **PhaseFieldX** version
- **DolfinX** version
- **ufl**, **basix**, and **numpy** versions
- **logging** module version

These calls are typically chained right after logger creation in solver entry points:

```python
logger = set_logger(result_folder_name)
log_system_info(logger)
log_library_versions(logger)

```

## Simulation Configuration Logging

Beyond environment metadata, PhaseFieldX captures user-defined simulation parameters through a standardized interface across all physics modules.

### Input Class `save_log_info` Methods

Every simulation **input class** (Phase-Field, Elasticity, Allen-Cahn) implements a **`save_log_info(self, logger)`** method that writes domain-specific parameters. For example, in [[`src/phasefieldx/Element/Phase_Field/Input.py`](https://github.com/castillonmiguel/phasefieldx/blob/main/src/phasefieldx/Element/Phase_Field/Input.py)](https://github.com/castillonmiguel/phasefieldx/blob/main/src/phasefieldx/Element/Phase_Field/Input.py#L52-L61):

```python
def save_log_info(self, logger):
    logger.info("Parameters:")
    logger.info(f"  l: {self.l}")

```

The Elasticity and Allen-Cahn input classes follow this identical pattern in [[`src/phasefieldx/Element/Elasticity/Input.py`](https://github.com/castillonmiguel/phasefieldx/blob/main/src/phasefieldx/Element/Elasticity/Input.py)](https://github.com/castillonmiguel/phasefieldx/blob/main/src/phasefieldx/Element/Elasticity/Input.py) and [[`src/phasefieldx/Element/Allen_Cahn/Input.py`](https://github.com/castillonmiguel/phasefieldx/blob/main/src/phasefieldx/Element/Allen_Cahn/Input.py)](https://github.com/castillonmiguel/phasefieldx/blob/main/src/phasefieldx/Element/Allen_Cahn/Input.py), respectively.

### Mesh Metadata with `log_model_information`

The **`log_model_information`** function (lines 127–131 in [`library_versions.py`](https://github.com/castillonmiguel/phasefieldx/blob/main/library_versions.py)) records geometric data such as mesh dimension, linking the computational domain properties to the specific run:

```python
log_model_information(msh, logger)

```

## Runtime Execution Monitoring

During the solution phase, solvers emit granular progress messages that enable debugging of convergence behavior and performance bottlenecks.

### Newton Solver Convergence Logging

The Newton solver in [[`src/phasefieldx/solvers/newton.py`](https://github.com/castillonmiguel/phasefieldx/blob/main/src/phasefieldx/solvers/newton.py)](https://github.com/castillonmiguel/phasefieldx/blob/main/src/phasefieldx/solvers/newton.py#L54-L71) implements **`save_log_info`** to record:

- Newton and Krylov tolerances
- Maximum iteration limits
- Convergence criteria

### Phase-Field Fracture Solver Tracing

The variational solver in [[`src/phasefieldx/Element/Phase_Field_Fracture/solver/solver_ener_variational.py`](https://github.com/castillonmiguel/phasefieldx/blob/main/src/phasefieldx/Element/Phase_Field_Fracture/solver/solver_ener_variational.py)](https://github.com/castillonmiguel/phasefieldx/blob/main/src/phasefieldx/Element/Phase_Field_Fracture/solver/solver_ener_variational.py#L277-L336) logs comprehensive execution data within each time step:

- **Analysis boundaries**: `"S t a r t i n g    A n a l y s i s"` markers
- **Physical quantities**: `Gamma0`, `tau(t)`, and time-step adjustments
- **Convergence tracking**: Newton iteration counts and residual norms
- **I/O operations**: XDMF and VTU file saving events

Example runtime logging pattern:

```python
if rank == 0 and logger:
    logger.info(f"\n\nGamma0 = {gamma0}")
    logger.info(f" start time: {start}")
    logger.info(" S t a r t i n g    A n a l y s i s ")
    # ... during iterations ...

    logger.info(f"Newton iterations: {newton_iterations}")
    logger.info(f"Residual norm: {residual_norm}")

```

### DolfinX Integration

PhaseFieldX also captures messages from the underlying finite-element library by setting the DolfinX C++ logger to `INFO` level:

```python
dolfinx.log.set_log_level(dolfinx.log.LogLevel.INFO)

```

This appears in solver files such as [`solver_ener_variational.py`](https://github.com/castillonmiguel/phasefieldx/blob/main/solver_ener_variational.py) (line 135), ensuring that low-level library warnings and convergence diagnostics appear in the same log stream.

## End-of-Run Summary and Cleanup

When simulations complete, **`log_end_analysis`** (lines 119–125 in [`library_versions.py`](https://github.com/castillonmiguel/phasefieldx/blob/main/library_versions.py)) writes a structured summary block:

```python
def log_end_analysis(logger, totaltime=0.0):
    logger.info("\n\n\n ====================================================")
    logger.info("\n\n End of computations")
    logger.info(" Analysis finished correctly.")
    logger.info(f" total simulation time: {totaltime}")
    logger.info("Analysis finished on %s" % time.strftime('%a %b %d %H:%M:%S %Y',
                                                          time.localtime()))

```

This provides a clear termination marker and wall-clock timing for benchmarking.

## Complete Implementation Example

The following pattern demonstrates the full logging lifecycle as implemented in [[`src/phasefieldx/Element/Phase_Field_Fracture/solver/solver.py`](https://github.com/castillonmiguel/phasefieldx/blob/main/src/phasefieldx/Element/Phase_Field_Fracture/solver/solver.py)](https://github.com/castillonmiguel/phasefieldx/blob/main/src/phasefieldx/Element/Phase_Field_Fracture/solver/solver.py):

```python
from phasefieldx.Logger.library_versions import (
    set_logger, log_system_info, log_library_versions,
    log_model_information, log_end_analysis
)
from phasefieldx.Element.Phase_Field.Input import Input

# 1. Initialize logger

results_folder = 'simulation_results'
logger = set_logger(results_folder)

# 2. Log environment and dependencies

log_system_info(logger)
log_library_versions(logger)

# 3. Log simulation parameters

sim_input = Input(l=2.5, results_folder_name=results_folder)
sim_input.save_log_info(logger)

# 4. Log mesh information (msh is a dolfinx mesh object)

# log_model_information(msh, logger)

# ... execute simulation ...

# 5. Finalize log with timing

log_end_analysis(logger, totaltime=123.45)

```

This produces a `simulation.log` file containing structured sections for environment metadata, parameters, runtime progress, and final timing.

## Summary

PhaseFieldX logging capabilities provide a comprehensive audit trail through the following mechanisms:

- **Centralized logger creation** via `set_logger` in [`library_versions.py`](https://github.com/castillonmiguel/phasefieldx/blob/main/library_versions.py), ensuring consistent file output and formatting
- **Environment capture** through `log_system_info` and `log_library_versions`, recording OS, Python version, and dependency versions (DolfinX, numpy, ufl, basix)
- **Configuration logging** via the `save_log_info` method pattern implemented across all Input classes (Phase-Field, Elasticity, Allen-Cahn)
- **Runtime tracing** with detailed Newton solver convergence data and physical quantity monitoring in fracture and phase-field solvers
- **Reproducibility markers** including mesh dimensions, start/end timestamps, and total wall-clock time via `log_end_analysis`
- **External library integration** by capturing DolfinX C++ logger output at `INFO` level

## Frequently Asked Questions

### Where does PhaseFieldX store simulation logs?

PhaseFieldX writes all log output to a file named **`simulation.log`** located inside the results folder specified during logger initialization. The path is constructed as `os.path.join(result_folder_name, 'simulation.log')` in the `set_logger` function, ensuring logs are co-located with simulation outputs for easy archival.

### How do I add custom parameters to the PhaseFieldX simulation log?

Extend the **`save_log_info(self, logger)`** method in your input class. Following the pattern in [[`src/phasefieldx/Element/Phase_Field/Input.py`](https://github.com/castillonmiguel/phasefieldx/blob/main/src/phasefieldx/Element/Phase_Field/Input.py)](https://github.com/castillonmiguel/phasefieldx/blob/main/src/phasefieldx/Element/Phase_Field/Input.py), add `logger.info()` calls for your custom attributes. The logger instance is passed through the solver hierarchy, making it available in any subclass of the input configuration objects.

### Does PhaseFieldX capture internal DolfinX messages?

Yes. PhaseFieldX sets the DolfinX C++ logger to `INFO` level using `dolfinx.log.set_log_level(dolfinx.log.LogLevel.INFO)` in solver entry points. This captures finite-element assembly messages, linear solver warnings, and mesh-related notifications in the same `simulation.log` file alongside Python-level logs.

### What log level does PhaseFieldX use by default?

The default log level is **`logging.INFO`**, set explicitly in `set_logger` within [`library_versions.py`](https://github.com/castillonmiguel/phasefieldx/blob/main/library_versions.py). This level captures standard operational messages while filtering routine debug output. You can modify this by calling `logger.setLevel(logging.DEBUG)` after initialization if you require verbose tracing for troubleshooting convergence issues.