# Mixed periodic/non-periodic BC for scalar transport

**URL:** <https://pyfr.discourse.group/t/mixed-periodic-non-periodic-bc-for-scalar-transport/693>\
**Category:** Development\
**Created:** [20 September 2022 07:05 UTC](https://pyfr.discourse.group/t/mixed-periodic-non-periodic-bc-for-scalar-transport/693 "2022-09-20T07:05:44Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![EitanA](https://avatars.discourse-cdn.com/v4/letter/e/bc79bd/32.png) [@EitanA](https://pyfr.discourse.group/u/EitanA)\
**Post date:** [20 September 2022 07:05 UTC](https://pyfr.discourse.group/t/mixed-periodic-non-periodic-bc-for-scalar-transport/693/1 "2022-09-20T07:05:45Z")

</div>

Hello all

I’ve added a passive scalar to PyFR and now I am looking to create periodic flow b.c. with inlet-outlet conditions for the passive scalar. Is there a simple way to achieve such a thing?  
Any help would be appreciated

Eitan

---

<div class="post-metadata">

**Author:** ![WillT](https://avatars.discourse-cdn.com/v4/letter/w/bc79bd/32.png) [@WillT](https://pyfr.discourse.group/u/WillT)\
**Post date:** [20 September 2022 08:17 UTC](https://pyfr.discourse.group/t/mixed-periodic-non-periodic-bc-for-scalar-transport/693/2 "2022-09-20T08:17:44Z")

</div>

I think this is quite challenging.

I think you would need to make sure that elements got added to both the interaface list and the bc int list and that any MPI communication also happened. You would then need to add some dependencies in the graph so that there isn’t a race condition on setting those interface points.

This will of course be dependent on how you implement the passives, but when I recently did this I added some optional buffers call pasv\_upts, pasv\_fpts, and pasv\_qpts. This methodology would work for that approach.

---

<div class="post-metadata">

**Author:** ![fdw](https://avatars.discourse-cdn.com/v4/letter/f/a587f6/32.png) [@fdw](https://pyfr.discourse.group/u/fdw)\
**Post date:** [20 September 2022 12:19 UTC](https://pyfr.discourse.group/t/mixed-periodic-non-periodic-bc-for-scalar-transport/693/3 "2022-09-20T12:19:00Z")

</div>

It can be done with moderate complexity.

First, mark the boundaries as being periodic in your mesh generation software and give them an appropriate name. Next, use the latest development version of PyFR which preserves periodic information in the mesh file and ensures that periodic interfaces always end up on the same MPI rank. Then, in the _internal_ interfaces class split apart the periodic faces into their own list. This is relatively simple to do since PyFR tags which internal interfaces are periodic. Finally, have the common flux for these interfaces computed by a custom kernel which does whatever you need to the passive scalar on the left- and right-hand sides.

Regards, Freddie.

---

<div class="post-metadata">

**Author:** ![EitanA](https://avatars.discourse-cdn.com/v4/letter/e/bc79bd/32.png) [@EitanA](https://pyfr.discourse.group/u/EitanA)\
**Post date:** [20 September 2022 16:37 UTC](https://pyfr.discourse.group/t/mixed-periodic-non-periodic-bc-for-scalar-transport/693/4 "2022-09-20T16:37:24Z")

</div>

Thank you.  
I will give it a try…

---

<div class="post-metadata">

**Author:** ![fredmorin18](https://yyz2.discourse-cdn.com/free1/user_avatar/pyfr.discourse.group/fredmorin18/32/755_2.png) [@fredmorin18](https://pyfr.discourse.group/u/fredmorin18)\
**Post date:** [21 February 2026 23:05 UTC](https://pyfr.discourse.group/t/mixed-periodic-non-periodic-bc-for-scalar-transport/693/5 "2026-02-21T23:05:12Z")

</div>

I was able to get this working in v2.1, but I’m not sure how to access the periodic boundary information in the solvers/base/system.py file in v3.0.

In v2.1, I mapped the periodic face pairs like so:

```auto
key = f'con_p{rallocs.prank}'

lhs, rhs = mesh[key].astype('U4,i4,i1,i2').tolist()

# Strip periodic BC indices
pidxs = list(mesh[key, 'periodic_0'])
new_lhs = [v for i, v in enumerate(lhs) if i in pidxs]
new_rhs = [v for i, v in enumerate(rhs) if i in pidxs]

pint_inters = self.pintinterscls(self.backend, new_lhs, new_rhs, elemap,
                                       self.cfg)

```

Which I found in @tdzanic ‘s repo [Comparing PyFR:develop...tarikdzanic:feature/rot\_periodic · PyFR/PyFR · GitHub](https://github.com/PyFR/PyFR/compare/develop...tarikdzanic:PyFR:feature/rot_periodic)

However, in v3.0, I only seem to be able to find the periodic BC info in the raw mesh data (`mesh.raw`), and I haven’t been able to figure out how to create a map from it. I would really appreciate any tips 🙂

Thanks,

Fred

---

<div class="post-metadata">

**Author:** ![fdw](https://avatars.discourse-cdn.com/v4/letter/f/a587f6/32.png) [@fdw](https://pyfr.discourse.group/u/fdw)\
**Post date:** [22 February 2026 13:58 UTC](https://pyfr.discourse.group/t/mixed-periodic-non-periodic-bc-for-scalar-transport/693/6 "2026-02-22T13:58:46Z")

</div>

As you noted the only way to access the periodic data is via `.raw`. This is on account of the fact that there are currently no consumers of this data in PyFR. Nevertheless, we decided to keep the capability when designing the new file format under the hope that it would be useful in the future. To understand the format it is worth reading through:

> **[File Format — Documentation](https://pyfr.readthedocs.io/en/latest/file_format.html)**

then, given an interface list you’ll need to go from etype/fidx to a cidx and from a local element number—eidx—to a global element number (or, alternatively, iterate through the periodic data and convert that to etype/fidx/eidx triples).

Also, note that when partitioning PyFR ensures that both sides of a periodic interface will end up on the same rank.

Regards, Freddie.

---

<div class="post-metadata">

**Author:** ![fredmorin18](https://yyz2.discourse-cdn.com/free1/user_avatar/pyfr.discourse.group/fredmorin18/32/755_2.png) [@fredmorin18](https://pyfr.discourse.group/u/fredmorin18)\
**Post date:** [23 February 2026 03:12 UTC](https://pyfr.discourse.group/t/mixed-periodic-non-periodic-bc-for-scalar-transport/693/7 "2026-02-23T03:12:07Z")

</div>

Thank you very much! I was able to get this working after some familiarizing with the mesh object and mesh file format. Here is my solution for posterity (I hard-coded hexes into it since that’s what I use, but it could be modified for other mesh types):

```auto
    def _load_int_inters(self, mesh, elemap):
        # Generate lhs and rhs lists of periodic face pairs (cidx, off)
        pfaces = mesh.raw['periodic']['0']
        p_lhs = [tuple(i) for i, k in pfaces]
        p_rhs = [tuple(k) for i, k in pfaces]
        
        # Decrement to get fidx and swap indices
        p_lhs = [('hex', int(i[1]), int(i[0]-1)) for i in p_lhs]
        p_rhs = [('hex', int(i[1]), int(i[0]-1)) for i in p_rhs]
        
        # Strip periodic BC indices
        lhs = [i for i in mesh.con[0] if i not in p_lhs + p_rhs]
        rhs = [i for i in mesh.con[1] if i not in p_lhs + p_rhs]
        
        int_inters = self.intinterscls(self.backend, lhs, rhs, elemap,
                                       self.cfg)
        
        return [int_inters]
        
    def _load_pint_inters(self, mesh, elemap):
        # Generate lhs and rhs lists of periodic face pairs (cidx, off)
        pfaces = mesh.raw['periodic']['0']
        p_lhs = [tuple(i) for i, k in pfaces]
        p_rhs = [tuple(k) for i, k in pfaces]
        
        # Decrement to get fidx and swap indices
        p_lhs = [('hex', int(i[1]), int(i[0]-1)) for i in p_lhs]
        p_rhs = [('hex', int(i[1]), int(i[0]-1)) for i in p_rhs]
        
        pint_inters = self.pintinterscls(self.backend, p_lhs, p_rhs, elemap,
                                       self.cfg)

        return [pint_inters]

```

The rest of the changes are similar to those Tarik’s repo (keeping in mind some updates from v2 to v3).

Cheers,

Fred
