<a id="guide-branch"></a>

<a id="how-to-branch-a-refinement-compare-the-branches-and-roll-back"></a>

# How to branch a refinement, compare the branches and roll back

Tag the node to start from, run the second strategy on `Refinement.branch`, compare the two ends with `RefinementTree.compare`, and return to the better one with `Refinement.checkout`.

Every stage and every edit commits a node to the refinement’s history, `Refinement.history`.
A node keeps the parameter values, the free set, the ties and the holds, so any of them can be restored and refined again.

<a id="fork-at-a-node"></a>

## Fork at a node

This runs two stages on the fluorapatite pattern that ships with rietx, tags the second, and then frees the peak widths in two ways from that one node:

```python

import rietx as rx
from rietx.examples import examples_dir

data = rx.read_pattern(examples_dir() / "FAP.XRA")
structure = rx.Structure.from_cif(examples_dir() / "fluorapatite.cif")
instrument = rx.Instrument.bragg_brentano(radiation="CuKa")
instrument.background = rx.BackgroundChebyshev.with_terms(6)
ref = rx.Refinement(structure, instrument)
limits = (15, 130)

ref.run_stage(data, rx.Stage("scale_bkg", ["phases.*.scale", "instrument.background.*"]),
              two_theta_limits=limits)
cell = ref.run_stage(data, rx.Stage("cell", ["instrument.zero_shift", "phases.*.cell.*"]),
                     two_theta_limits=limits)
ref.history.tag(cell.node_id, "cell")

# Strategy A: free the five width terms together.
together = ref.run_stage(data, rx.Stage("profile", ["instrument.profile.*"]),
                         two_theta_limits=limits)

# Strategy B, on a branch from the tagged node: free w first, then all five.
alt = ref.branch("cell")
alt.run_stage(data, rx.Stage("profile_w", ["instrument.profile.w"]), two_theta_limits=limits)
w_first = alt.run_stage(data, rx.Stage("profile", ["instrument.profile.*"]),
                        two_theta_limits=limits)
```

`ref.history.tag` names a node, and every call that takes a node id takes the name instead.
`ref.branch("cell")` returns a second `Refinement` over the same history, positioned at the tagged node.
Each object keeps its own position, so `ref` stays at the end of strategy A while `alt` runs strategy B.

<a id="compare-the-branches"></a>

## Compare the branches

`RefinementTree.compare` returns one row per node, and `RefinementTree.best` returns the node with the lowest Rwp in the whole tree:

```python
tree = ref.history
tree.tag(together.node_id, "together")
tree.tag(w_first.node_id, "w_first")

for row in tree.compare(["together", "w_first"]):
    print(f"{row['id']}  {row['action']:<14} free {row['n_free']}  Rwp {row['rwp']:.4f}")
print("best:", tree.best("rwp").id)
```

```text
n0003  stage:profile  free 15  Rwp 0.0947
n0005  stage:profile  free 15  Rwp 0.0947
best: n0005
```

Both ends refine the same 15 parameters and reach Rwp 0.0947, so on this pattern the two orders find one fit.
`best` returns `n0005`, lower by 0.00002.
`best` takes the name of any other `Statistics` field, such as `"gof"` or `"chi2"`, and skips the nodes that ran no fit.

`RefinementTree.diff` lists the parameters whose values differ between two nodes, as `(first, second)` pairs.
Here it is narrowed to the five width terms, in deg²:

```python
for path, (a, b) in tree.diff("together", "w_first").items():
    if path.startswith("instrument.profile."):
        print(f"{path:<22} {a:.5f}  {b:.5f}")
```

```text
instrument.profile.u   0.00187  0.00187
instrument.profile.v   -0.00320  -0.00320
instrument.profile.w   0.00324  0.00324
instrument.profile.x   0.03102  0.03101
instrument.profile.y   0.03371  0.03372
```

The full list also holds the background, zero shift, lattice parameters and scale.
Every value in it is within 0.05 % of the other branch’s.

<a id="roll-back-and-carry-on"></a>

## Roll back and carry on

`Refinement.checkout` puts the working models back to a node’s values.
The node itself is unchanged, and the next stage commits on top of it:

```python
ref.checkout("w_first")
ref.run_stage(data, rx.Stage("biso", ["phases.*.atoms.*.biso"]), two_theta_limits=limits)
print(tree.summary())
```

```text
tcb8d2285  7 nodes  data=FAP.XRA
 n0000  root                   —
 n0001  stage:scale_bkg        Rwp 0.6050
 n0002  stage:cell             Rwp 0.4919  [cell]
├─  n0003  stage:profile          Rwp 0.0947  [together]
└─  n0004  stage:profile_w        Rwp 0.1930
    n0005  stage:profile          Rwp 0.0947  [w_first]
   *n0006  stage:biso             Rwp 0.0943
```

`RefinementTree.summary` draws the tree with each node’s Rwp and its tags in brackets.
A fork is indented, and `*` marks the head, the node the last commit or checkout moved to.
Strategy A’s end, `n0003`, stays in the tree and can be checked out again later.

The same call undoes a stage without a branch: check out the node before it and run something else.
The stage you undid stays in the tree as a branch nobody continued.

<a id="recompute-a-node"></a>

## Recompute a node

A node records the Rwp the solver reached, on a model whose reflection list and peak windows were fixed when its stage started.
`rx.replay` recomputes a node’s curves and statistics from its values, without refining and without adding a node:

```python
for name in ("together", "w_first"):
    replayed = rx.replay(tree, name, data)
    print(f"{name:<9} recorded {tree[name].rwp:.4f}  replayed {replayed.statistics.rwp:.4f}")
```

```text
together  recorded 0.0947  replayed 0.0970
w_first   recorded 0.0947  replayed 0.0970
```

The difference comes from rebuilding the reflection list and windows at the stage’s final values.
The `RefinementResult` a stage returns is computed as a replay is, so `together.statistics.rwp` is 0.0970.
A result carries `FROZEN_COMPILE_STALE` when its χ² and its node’s differ by more than 1 %.
Both results carry it here, at 5.0 %, and the two strategies stay within 0.0001 of each other on either measure.

`replay` refuses a pattern other than the one the history was recorded against, and names both.

<a id="combine-two-branches"></a>

## Combine two branches

`Refinement.cherry_pick` takes a stage node from anywhere in the tree and runs the same stage from the current position.
It repeats the recorded stage, with the parameters it freed and its solver settings, and not the values that stage reached.
Only a `stage` node can be cherry-picked.

`Refinement.merge` combines another node’s parameter values into the current one, path by path, against the latest node both descend from.
A value changed on one side only takes that side’s value, and one changed on both takes the side `prefer` names (`"theirs"` by default).
The phases, background, free set and mode come whole from the preferred side, so read `Refinement.structure` back after a merge across a model edit.

<a id="keep-the-history-between-sessions"></a>

## Keep the history between sessions

A project keeps the history in `history.jsonl`, and reopening it resumes at the last node ([About a project](../concepts/project.html.md)).
Outside a project, `rx.Refinement(..., history="fit.jsonl")` appends each node to that file as it is committed.
`rx.RefinementTree.load` reads one back, and `rx.Refinement.from_node(tree, "head")` opens a refinement at a node.

<a id="see-also"></a>

## See also

- [How to run a plan one stage at a time](stage.html.md) shows where in a plan you would stop to branch.
- [How to choose the next parameter to free](next.html.md) compares two branches of one node to measure a candidate parameter.
- [About a fit](../concepts/fit.html.md) describes what a node records.
