PKR Core
Use Case 8 min read

Diffusion adds up, permeability does not

Two layers of different porosity, stacked into one structure with one call, then measured both ways. Predicting the stack from the layer numbers worked for effective diffusion, landing within 2.9 % on all five seeds. The same arithmetic on permeability was off by anywhere from 11 % low to 16 % high, and the sign flipped between seeds.

  • Build Structures
  • Analyze Properties
  • Explore Design Space
Three cross sections through one stacked structure, with the dense layer above the marked join and the coarse layer below it.

What you can do

You can stack measured structures into one structure and measure that. One call joins two stored structures along an axis and hands back a new structure you can send to any analysis.

  • POST /api/v1/compose takes two or more structureIds, a mode and an axis, and returns a composed structureId.
  • The axes you are not stacking along have to match, so a 64x64x32 layer stacks onto another 64x64x32 layer and a mismatch comes back as a 400.
  • Set remapMaterials to preserve and the material ids you measured the layers with still mean the same thing.
  • POST /api/v1/metrics, /api/v1/diffusion and /api/v1/permeability all accept the composed structureId directly.

That gives you a cheap test you can run on your own recipe. Measure each layer, predict the stack, then measure the stack and see how far apart they are.

Why it matters

Grading porosity through the thickness is a live design idea for gas diffusion layers and porous transport layers. Whether it helps depends on numbers for the graded stack, not for any one layer.

What you usually have is the layer numbers. Each layer was made and measured on its own, and combining them with a mixing rule is the obvious shortcut. The question is whether that shortcut survives contact with your structure.

It survived for one of the two properties measured here and not the other. Which means the answer is a property of your recipe, and worth ten minutes of checking before you lean on it.

Three structures, one mean porosity

The comparison only works if the stacked and uniform structures hold the same amount of pore space. They did, to better than 0.2 points on every seed.

  • Layer A is a 64x64x32 sphere packing at 45 % target loading, the denser layer.
  • Layer B is the same recipe at 25 % target loading, the coarser layer.
  • The stack is layer A and layer B joined along z, giving a 64x64x64 box.
  • The uniform structure is one 64x64x64 packing at 35 % loading, built to match the stack on mean porosity.
  • Everything else is held fixed: the same particle-packing example, 15 % overlap and one seed per replicate.
StructureGridPorosity, mean of 5 seedsPorosity, spread
Layer A alone64x64x3254.93 %54.82 to 54.99 %
Layer B alone64x64x3274.92 %74.83 to 74.99 %
Stacked A + B64x64x6464.92 %64.82 to 64.98 %
Uniform64x64x6464.97 %64.92 to 65.00 %
Three square cross sections through one stacked structure, cut at x = 16, 32 and 48. Blue is solid and mint green is pore. In every panel the upper half is visibly more crowded with blue than the lower half, and a red horizontal line marks the join between them at z = 32.
The composed structure, cut three ways. The dense layer sits above the red line and the coarse layer below it. Nothing was blended at the join.

The whole experiment was then repeated on five seeds. One replicate is five structures, one compose call and twelve analyses, which came to 16 requests and about 7 seconds of API time.

For diffusion, the layer numbers add up

The series rule predicted the stacked diffusivity to within 2.9 % on every seed. That rule combines the two layers as resistances in series, weighted by thickness, which here is half and half.

Quantity, along zMean of 5 seedsGap from the measured stack
Layer A alone0.825
Layer B alone0.910
Series rule prediction0.865+0.8 to +2.9 %
Stacked, measured0.881
Uniform, measured0.889−0.1 to −2.0 %

Effective diffusion is reported relative to the free diffusivity of the pore phase, so 0.881 means the stack passes 88 % of what open space would. The prediction ran slightly low on all five seeds, which is a bias rather than noise.

A bias that small is usually not worth chasing. If you can tolerate a 3 % error on diffusivity, you can compose your layer measurements and skip building the stack.

For permeability, the average hides the problem

The same rule applied to permeability missed by −11 % on one seed and +16 % on another. There is no correction to apply, because the error does not keep its sign.

Dot plot of four comparisons with five dots each, one per seed, on an axis running from minus 25 to plus 20 percent with a vertical line at zero. The two effective diffusion rows sit in tight clusters close to zero. The two permeability rows are spread wide, one straddling zero from minus 11 to plus 16 and one entirely to the left of zero from minus 21 to minus 4.
The same four comparisons on five seeds. The diffusion rows are tight and the permeability rows are not. Only the width of each row matters here.

Averaging the five seeds makes it look fine, and that is the trap. The predicted permeability averages 0.363 µm² and the measured stack averages 0.353 µm², which is under 3 % apart.

Permeability along z, µm²Mean of 5 seedsLowest seedHighest seed
Layer A alone0.2730.2340.309
Layer B alone0.5430.5050.559
Series rule prediction0.3630.3200.398
Stacked, measured0.3530.3200.373
Uniform, measured0.4000.3820.435

A single run gives you one seed, not the average of five. On this recipe that one run would have told you the rule works beautifully or fails badly, with no way to tell which from the run itself.

Same pore space, less permeable

The stacked structure was less permeable than the uniform one on all five seeds, by 4.3 % to 21.4 %. Both hold the same mean porosity to within 0.2 points, so porosity is not what separates them.

Diffusion barely noticed the same rearrangement. Stacked came in 0.1 % to 2.0 % below uniform, close enough to the run-to-run scatter that the gap is hard to call.

The reading is that the dense layer sets the flow and the coarse layer cannot make up for it. Transport tortuosity was 1.21 in layer A against 1.10 in layer B, so the two layers are not far apart on path length. The cost is in the narrow throats rather than in a longer route.

For a graded design that matters directly. Quoting a target mean porosity does not pin down the permeability you will get, and the grading itself costs you something on this recipe.

Measuring across the layers instead of through them

Measuring along x puts the two layers side by side rather than end to end, so the parallel rule should apply instead of the series rule. For diffusion this direction cannot settle anything.

The two rules only disagree by 0.2 to 0.3 % for these layers, because layer A and layer B are close on diffusivity to begin with. Any measurement lands between them.

For permeability the two rules are 9 % to 15 % apart, so the test is real, and the measurement matched neither. The parallel prediction was off by +0.1 % to −11.8 % across the five seeds, which is the same scattered picture as before.

The stacked structure was more permeable across the layers than through them on all five seeds, by 3 % to 25 %. The uniform structure straddled 1.0 on the same comparison, so some of that spread is the measurement rather than the geometry.

Running it on your own recipe

Generate each layer with storeStructure set, compose the ids, then send the composed id to the same analyses you ran on the layers.

POST /api/v1/generate     { "recipe": { ...45 % loading, nz 32 }, "storeStructure": true }
-> structureId: "A"
POST /api/v1/generate     { "recipe": { ...25 % loading, nz 32 }, "storeStructure": true }
-> structureId: "B"

POST /api/v1/compose
{
  "structures": [ { "structureId": "A" }, { "structureId": "B" } ],
  "mode": "stack",
  "axis": "z",
  "remapMaterials": "preserve"
}
-> structureId, structureSummary.grid = { nx: 64, ny: 64, nz: 64 }

POST /api/v1/diffusion
{
  "structureId": "<composed>",
  "request": {
    "version": "phase0.v1",
    "analysisType": "effectiveDiffusion",
    "inputs": { "grid": { "nx": 64, "ny": 64, "nz": 64 },
                "materialProperties": { "0": 1 } },
    "params": { "direction": "z", "method": "graph-network", "poreMaterialIds": [0] },
    "requestedOutputs": [ "summary" ]
  }
}
-> summary.value, transportTortuosity.value
  • Put the diffusivity on material 0, the pore phase. Put it on the solid instead and summary.value is unchanged, but transportTortuosity comes back unavailable with the reason "bulk diffusivity is not positive".
  • Keep remapMaterials on preserve. The default reassigns ids per input, so two layers whose solid was material 1 come back as materials 1 and 2. Every later material reference then points at the wrong phase.
  • Match the grid on the axes you are not stacking. A 12-wide layer stacked onto a 16-wide one is refused with "Structure 2 has nx=12; expected 16 for z-axis stacking".
  • Check the composed porosity against your uniform reference before comparing anything. If they are more than a point apart, the comparison is about porosity and not about layering.
  • Run at least three seeds. One seed cannot tell you whether a gap is the geometry or the run.

The loading has to be written into several places in the recipe at once. The generator reads geometryParams, fillerConfigs and distributionRules, so setting only the top-level volumeFraction leaves the packing where the example had it.

What this does not settle

This is two layers at half and half, on one example recipe, in a 64³ box. A thinner dense layer or a sharper porosity contrast could move both results, and the series rule was only ever tested at one thickness ratio.

Both solvers work on a reduced network rather than the full grid. The permeability scatter may be partly the network extraction rather than the structure, and a full-grid solve is the way to separate those.

The join itself is abrupt. Real graded layers blend over some distance, and nothing here says whether a blended join behaves like this one.

Try it in PKR Core.

Open PKR Core