Skip to content

Barrier filter extinguishes a cell that was only blocked for one period #235

Description

@RodrigoMahalufRecasens

Found while porting the barrier/breaching mechanism from breaching-shading-fat into our Kitral fork (C3F-K). The same defect is present here, so passing it upstream.

The problem

In the blocked-arc filter in Cell2Fire::SendMessages:

// Cell2Fire/Cell2Fire.cpp, breaching-shading-fat
aux_list.swap(keep);
if (aux_list.empty())
{
    this->burnedOutList.push_back(fromId);
    continue;
}

When a barrier blocks every destination of a given period, the cell is pushed to burnedOutList and therefore removed from burningCells permanently.

But "the barrier blocked all my targets this period" is not the same as "I have nowhere left to spread". aux_list holds the neighbours the fire reached during this period, not all of the cell's available neighbours. In a later period — with a different wind direction, or simply with more accumulated nb_progress — the same cell can reach a different neighbour whose centre-to-centre transition does not cross the barrier. Extinguishing it discards that.

Why it matters: it puts cliffs in the objective

The effect is not a small bias. Sweeping the wind adjustment weight on a real case (Cuesta Barriga, Chile: 130 OSM road polylines as 8 m barriers over a 212x166 grid at 30 m), with the slope weight at 0:

wind weight burnt cells, no barriers burnt cells, with barriers
0.4 17112 7487
0.5 16856 7104
0.6 16788 2
0.8 16891 6214
1.0 17113 2
1.5 18257 6109

Without barriers the response is smooth. With barriers it collapses to 2 cells at some weights and recovers at others — discontinuous and non-monotone. Marginal fires die outright: the fire fails to establish because the few cells burning early get extinguished the first time a road blocks their targets.

This matters for --breach-factor work too: any optimisation over adjustment factors sees a discontinuous objective, and derivative-free local methods (BOBYQA and friends) behave badly on it.

The fix we applied

Distinguish "manageFire produced nothing" from "the filter emptied the list", with one exception so the cost stays flat:

bool blockedThisPeriod = false;
...
aux_list.swap(keep);
if (aux_list.empty())
{
    blockedThisPeriod = true;
    // If NO available neighbour is reachable at all, the cell can never spread and
    // should be extinguished. Without breaching a blocked arc is blocked forever, so
    // this is exact; with breaching the flame length can grow, so skip the check.
    if (this->args.BreachFactor <= 0.0 && this->args.SpotFactor <= 0.0)
    {
        bool any = false;
        for (int i = 0; i < cell_ptr->nb_count && !any; i++)
            if (cell_ptr->nb_available[i] &&
                !this->blockedArcs.count(this->arcKey(fromId, cell_ptr->nb_ids[i])))
                any = true;
        if (!any) blockedThisPeriod = false;   // trapped: let it burn out
    }
}
...
if (aux_list.size() == 0 && !blockedThisPeriod)
    this->burnedOutList.push_back(fromId);

Without that exception, blocked cells stay in burningCells and are reprocessed every period: on our case a run went from 0.51 s to 1.07 s. With it, 0.515 s against 0.507 s.

Results after the fix

  • The sweep above becomes smooth: 7574, 7180, 6889, 6529, 6328, 6392, 7217.
  • Your diagonal-leak test is unaffected: on a 40x40 homogeneous grid with a complete anti-diagonal firebreak and ignition in the opposite corner, still 569 cells burnt and 0 of 780 beyond the barrier, with the 39 diagonal crossings blocked as before.
  • With no barriers and no firebreaks configured, output is byte-identical over 75 files (data/Homogeneous and data/Portezuelo, 3 sims, fixed seed).

Note our fork differs from yours in ways that may matter to how you apply this: we dropped the ellipse (it overestimated BROS), we only ported the firebreak/diagonal part plus arc widths, and our barrier geometry is processed in Python because the width comes from a per-feature attribute and we need GPKG. The defect itself is in the shared filter logic, so it should apply unchanged.

Happy to open a PR against breaching-shading-fat if that is useful.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions