<!-- DO NOT EDIT. -->
<!-- THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. -->
<!-- TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: -->
<!-- "auto_examples/network_generator/plot_custom_crosslinking_callback.py" -->
<!-- LINE NUMBERS ARE GIVEN BELOW. -->

#### NOTE
[Go to the end](#sphx-glr-download-auto-examples-network-generator-plot-custom-crosslinking-callback-py)
to download the full example code.

<a id="sphx-glr-auto-examples-network-generator-plot-custom-crosslinking-callback-py"></a>

# Custom Crosslinking with Callback Functions

This example demonstrates how to use the [`link_strands_callback()`](../../api/pylimer_tools_cpp.MCUniverseGenerator.html.md#pylimer_tools_cpp.MCUniverseGenerator.link_strands_callback)
method to create custom crosslinking procedures
as an alternative to the built-in linking methods,
[`link_strands_to_conversion()`](../../api/pylimer_tools_cpp.MCUniverseGenerator.html.md#pylimer_tools_cpp.MCUniverseGenerator.link_strands_to_conversion) and
[`link_strands_to_soluble_fraction()`](../../api/pylimer_tools_cpp.MCUniverseGenerator.html.md#pylimer_tools_cpp.MCUniverseGenerator.link_strands_to_soluble_fraction).
The callback function allows users to control when the crosslinking process should stop based on
any criteria they can implement.

The callback function receives two parameters:
- The current MCUniverseGenerator instance
- The current step number

And should return one of the BackTrackStatus values:
- STOP: Stop the linking process
- TRACK_FORWARD: Continue linking forward
- TRACK_BACKWARD: Track backward in the linking process

<!-- GENERATED FROM PYTHON SOURCE LINES 23-35 -->
```Python
import math

import matplotlib.pyplot as plt

from pylimer_tools.calc.miller_macosko_theory import predict_gelation_point
from pylimer_tools.io.bead_spring_parameter_provider import (
    ParameterType,
    get_parameters_for_polymer,
)
from pylimer_tools_cpp import BackTrackStatus, MCUniverseGenerator, MEHPForceBalance2
```

<!-- GENERATED FROM PYTHON SOURCE LINES 36-41 -->

## Example 1: Stop at Conversion OR Soluble Fraction

Using a callback allows for more complex logic, such as
stopping the process based on multiple criteria as illustrated here.

<!-- GENERATED FROM PYTHON SOURCE LINES 41-128 -->
```Python
# Get parameters for PDMS polymer density and bead distance
params = get_parameters_for_polymer(
    "PDMS", parameter_type=ParameterType.GAUSSIAN)

n_xlinks = 500  # Number of crosslinkers
n_strands = 1000  # Number of strands
strand_length = 50  # Length of each strand

n_atoms_total = n_strands * strand_length + n_xlinks
volume = n_atoms_total / params.get_bead_density()

generator = MCUniverseGenerator(
    volume ** (1 / 3),
    volume ** (1 / 3),
    volume ** (1 / 3),
)
generator.set_seed(42)

# Add crosslinkers and strands
generator.add_crosslinkers(n_xlinks, 4, 2)  # functionality 4
generator.add_strands(n_strands, [strand_length] * n_strands, 1)

target_conversion = 0.9  # Stop at 95% conversion
target_wsol = 0.2  # Stop if soluble fraction is below 20%

# record data throughout the process
conversions = []
wsol_values = []
steps = []


def conversion_callback(gen, steps_remaining):
    """
    Callback function to monitor crosslinking progress.
    This function checks the current crosslinker conversion and soluble fraction,
    and decides whether to stop the linking process.

    .. todo:
      Implement the search in a binary search fashion to improve efficiency
    """
    step = n_strands * 2 - steps_remaining
    assert isinstance(gen, MCUniverseGenerator), (
        "Callback must receive a MCUniverseGenerator instance"
    )
    current_conversion = gen.get_current_crosslinker_conversion()
    conversions.append(current_conversion)
    steps.append(step)

    if current_conversion >= predict_gelation_point(r=1, f=4, b2=1):
        prev_wsol = wsol_values[-1] if wsol_values else 1.0
        # Check if we need to run force balance
        if (step % 10 == 0) or (
            math.isclose(prev_wsol, target_wsol, rel_tol=1e-1, abs_tol=5e-2)
        ):
            fb2 = gen.get_force_balance2()
            assert isinstance(fb2, MEHPForceBalance2), (
                "Force balance must be MEHPForceBalance2"
            )
            fb2.run_force_relaxation()
            current_wsol = fb2.get_soluble_weight_fraction()
        else:
            current_wsol = prev_wsol
    else:
        # skip running force balance while
        # we don't even expect to have a network
        current_wsol = 1.0
    wsol_values.append(current_wsol)

    if current_conversion >= target_conversion or current_wsol <= target_wsol:
        print(
            "  Target conversion {:.1f} or soluble fraction {:.1f} reached at step {}.".format(
                target_conversion, target_wsol, step
            )
        )
        return BackTrackStatus.STOP
    else:
        return BackTrackStatus.TRACK_FORWARD


generator.link_strands_callback(conversion_callback, 1.0)

final_conversion = generator.get_current_crosslinker_conversion()
print(
    f"  Final conversion: {final_conversion:.3f} and w_sol: {wsol_values[-1]:.3f}")
```

```none
Target conversion 0.9 or soluble fraction 0.2 reached at step 1473.
Final conversion: 0.736 and w_sol: 0.198
```

<!-- GENERATED FROM PYTHON SOURCE LINES 129-134 -->

## Visualizing the Results

Let’s plot the evolution of crosslinker conversion
and soluble fraction over the steps.

<!-- GENERATED FROM PYTHON SOURCE LINES 134-145 -->
```Python
fig, ax = plt.subplots()

ax.plot(steps, [c * 100 for c in conversions], label="Conversion")
ax.plot(steps, [wsol * 100 for wsol in wsol_values], label="Soluble Fraction")
ax.set_xlabel("Step")
ax.set_ylabel("Percent [%]")
ax.set(xlim=(0, max(steps)), ylim=(0, 100))
ax.legend()

plt.show()
```

**Total running time of the script:** (0 minutes 0.903 seconds)

<a id="sphx-glr-download-auto-examples-network-generator-plot-custom-crosslinking-callback-py"></a>

[`Download Jupyter notebook: plot_custom_crosslinking_callback.ipynb`](_downloads/73de68c50b9a1c7231f7207c68c28f2b/plot_custom_crosslinking_callback.ipynb)

[`Download Python source code: plot_custom_crosslinking_callback.py`](_downloads/8575d1f8f9eb34f920de0a0e75ea12ed/plot_custom_crosslinking_callback.py)

[`Download zipped: plot_custom_crosslinking_callback.zip`](_downloads/d5ef3ff53de752850999d8a9c0b5cf5a/plot_custom_crosslinking_callback.zip)

[Gallery generated by Sphinx-Gallery](https://sphinx-gallery.github.io)
