PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
 
Loading...
Searching...
No Matches
Boundary Conditions Guide

This page is the detailed reference for boundary-condition authoring in case.yml. It documents what is currently supported by the Python conductor and what is currently wired in C.

1. Boundary-Condition Grammar

case.yml -> boundary_conditions is one list with 6 faces.

Face names:

  • -Xi, +Xi, -Eta, +Eta, -Zeta, +Zeta

Every block must provide all six faces exactly once.

2. Boundary Types

A face's type says what kind of boundary it is; its handler says how that boundary is computed. The type selects which handlers are legal and fixes the precedence used where faces meet at an edge.

Value Maps to Status
inletINLETsupported
outletOUTLETsupported
periodicPERIODICsupported
symmetrySYMMETRYlatent - not selectable
wallWALLsupported
Warning
symmetry is accepted by the type map but no public handler supports it, so any complete configuration using it is rejected. It is listed as latent rather than removed because the C enum still carries SYMMETRY; it is not a capability you can select today.

wall

Identity. type: wall -> WALL -> priority BC_PRIORITY_WALL.

What it does. Marks a face as a solid surface. Legal handler: noslip.

When to choose it. Any impermeable physical surface.

Parameters it owns. None; parameters belong to the handler.

Interactions. Wall loses to inlet and farfield at shared edges and wins over outlet. Wall functions are configured separately under physics.turbulence.wall_function.

Diagnostics. The startup banner lists the resolved type and handler per face.

Evidence. Unit verified - make unit-boundaries. Analytically verified - duct-poiseuille-picard-2026-09-18.

Limitations. MOVING_WALL is present in the C enum and accepted by the C compatibility check, but no public handler exposes it.

inlet

Identity. type: inlet -> INLET -> priority BC_PRIORITY_INLET.

What it does. Marks a face where flow enters with a prescribed condition. Legal handlers: constant_velocity, parabolic, prescribed_flow.

When to choose it. Any face where you specify what enters the domain.

Parameters it owns. None; parameters belong to the handler.

Interactions. Highest precedence, so an inlet wins at every shared edge. A domain with an inlet needs an outlet to conserve against.

Diagnostics. Startup banner reports the resolved handler and its configured values per face.

Evidence. Unit verified - make unit-boundaries. Production exercised in examples/flat_channel. Analytically verified - duct-poiseuille-picard-2026-09-18.

Limitations. Time-varying inlet types exist in the C enum but are not exposed.

outlet

Identity. type: outlet -> OUTLET -> priority BC_PRIORITY_OUTLET.

What it does. Marks a face where flow leaves. Legal handler: conservation.

When to choose it. The downstream face of any through-flow domain.

Parameters it owns. None; parameters belong to the handler.

Interactions. Lowest precedence, so inlet and wall win at shared edges. Its flux is derived from global conservation rather than prescribed.

Diagnostics. The inlet/outlet flux balance is reported at startup.

Evidence. Unit verified - make unit-boundaries. Production exercised in examples/flat_channel. Analytically verified - duct-poiseuille-picard-2026-09-18.

Limitations. A pressure-specified outlet exists in the C enum but is not exposed; no handler implements it. What a pressure condition needs is at 6b. Pressure Boundary Conditions.

periodic

Identity. type: periodic -> PERIODIC.

What it does. Marks a face as one half of a periodic pair. Legal handlers: geometric, constant_flux, initial_flux.

When to choose it. Any statistically homogeneous direction.

Parameters it owns. None; parameters belong to the handler.

Interactions. Periodic axes are derived from paired PERIODIC conditions before DMDA creation, so both faces must declare it and the grid must satisfy the translation contract. models.domain does not accept periodic flags. Both faces of a driven pair must carry the same driven handler.

Diagnostics. The startup banner reports the derived periodic axes and each validated translation.

Evidence. Unit verified - make unit-periodic. Regression verified - make smoke-periodic. Analytically verified - tgv2d-picard-order-2026-09-18: a triply periodic Taylor-Green vortex at second order in time and space; periodic-channel-laminar-picard-2026-09-18: a channel periodic in two directions reproduces the laminar profile at second order and stays uniform along both periodic axes to 5e-12.

Limitations. Requires a matching translation between paired faces; see 2. Grid Contract. The wall-bounded pseudo-time stall once recorded here did not reproduce at current code (5.7 Known limitations); turbulent periodic channels at production resolution have not been re-characterized.

3. Supported Handlers

The table below is generated from BC_HANDLER_SPECS in picurv_cli/core.py, the validation layer that decides what a case file may actually contain. It is regenerated by make docs-inventory and checked by make audit-capability, so it cannot drift from the validator.

Value Applies to Required parameters Optional parameters
conservationOUTLETnonenone
constant_fluxPERIODICtarget_fluxapply_trim, enforce_seam_flux
constant_velocityINLETvx, vy, vznone
geometricPERIODICnonenone
initial_fluxPERIODICnoneapply_trim, enforce_seam_flux
noslipWALLnonenone
parabolicINLETv_maxnone
prescribed_flowINLETsourcenone

Rules that apply across all handlers:

  • Legacy params.vector and params.velocity are rejected; use scalar vx/vy/vz.
  • Unknown parameters are rejected per handler rather than ignored.
  • A driven handler (constant_flux or initial_flux) must be configured, with the same handler, on both faces of the periodic pair.
  • Velocities and fluxes are given in physical units in YAML and non-dimensionalized before reaching C. See 5. Non-Dimensionalization Before C Input.

4. Handler Entries

Each entry below follows the capability-entry contract defined in Documentation Extension Framework: what it does, when to choose it over its siblings, the parameters it owns, its interactions, how to tell it is working, the evidence behind it, and its limitations.

noslip

Identity. handler: noslip on a WALL face -> BC_HANDLER_WALL_NOSLIP -> Create_WallNoSlip.

What it does. Enforces zero relative velocity at the wall. Both the contravariant face flux and the reconstructed Cartesian velocity are driven to zero on the boundary, so no mass crosses the face and no slip is permitted along it.

When to choose it. The default for any solid stationary surface. If the wall moves, or if you intend to model the near-wall layer rather than resolve it, this is not the handler you want - but note that neither alternative is exposed yet (see Limitations).

Parameters it owns. None. noslip takes no parameters, and supplying any is a validation error.

Interactions. Wall faces participate in the boundary priority ordering (BC_PRIORITY_WALL) so that inlet and farfield conditions win at shared edges. Wall functions are configured separately under physics.turbulence.wall_function and are not part of this handler.

Diagnostics. The startup banner lists each face and its resolved handler. A wall that is silently not applied shows up as non-zero velocity in the first cell layer; LOG_FIELD_MIN_MAX on Ucat is the quickest check.

Evidence. Unit verified - make unit-boundaries exercises the handler factory and direct handler behavior in tests/c/test_boundaries.c. Production exercised in every shipped wall-bounded example. Analytically verified - duct-poiseuille-picard-2026-09-18: the walls of a laminar square duct reproduce the Poiseuille profile at second order.

Limitations. MOVING_WALL exists in the C enum but is not exposed through the validator, so a moving wall cannot currently be configured from YAML.

constant_velocity

Identity. handler: constant_velocity on an INLET face -> BC_HANDLER_INLET_CONSTANT_VELOCITY -> Create_InletConstantVelocity.

What it does. Imposes a uniform, time-constant normal speed across the whole inlet face. The C handler selects the component normal to the logical face (vx, vy, or vz) and aligns it with the local metric normal.

When to choose it. The simplest inlet, and the right one for a uniform free-stream or a plug-flow entry. Choose parabolic instead when you want a developed profile without supplying data, or prescribed_flow when you have real profile data.

Parameters it owns. vx, vy, vz - all three required by the shared input schema, in m/s. Only the component normal to the selected face is currently applied; the legacy vector/velocity spellings are rejected.

Interactions. Inlet has the highest boundary priority, so it wins over wall and outlet at shared edges. The outlet must be able to pass the mass this inlet introduces; pair it with conservation.

Diagnostics. The startup banner prints the configured vector for the face as [vx,vy,vz]. It does not report a derived or integrated flux; runtime flux reporting is separate from the banner.

Evidence. Unit verified - make unit-boundaries. Production exercised in examples/flat_channel. Analytically verified - duct-inlet-handlers-2026-09-21: the inlet delivers bulk velocity 1.00000 and the duct develops to Poiseuille flow at order 1.87.

Limitations. Uniform in space and constant in time.

parabolic

Identity. handler: parabolic on an INLET face -> BC_HANDLER_INLET_PARABOLIC -> Create_InletParabolicProfile.

What it does. Imposes an analytic parabolic profile across the inlet face, scaled so the centreline reaches v_max and the velocity falls to zero at the bounding walls.

When to choose it. When you want a developed-looking channel or pipe inlet without generating profile data. It is an analytic approximation, not a solution of the flow you are about to run, so it is a starting condition rather than a validated developed profile. For a genuinely developed inlet, use prescribed_flow with a slice from a precursor run.

Parameters it owns. v_max - required, in m/s, the centreline speed.

Interactions. Same inlet priority and outlet-pairing considerations as constant_velocity.

Diagnostics. The startup banner prints the configured v_max for the face, not an integrated flux. If the face is not bounded by walls on the axes the profile assumes, the profile will not vanish at the edges - inspect the inlet plane.

Evidence. Unit verified - make unit-boundaries. Analytically verified - duct-inlet-handlers-2026-09-21: the delivered bulk velocity matches the sampled product parabola on n cells per side, v_max ((2n^2 + 1) / (3n^2))^2, which tends to (4/9) v_max, to five digits, and the duct develops to Poiseuille flow at order 1.86. Before the 2026-09-21 fix the profile vanished at the wall-adjacent cell centres and delivered 27% and 13% too little.

Limitations. The profile is a product of logical-space parabolas, one per wall-bounded axis, not the duct solution; it is uniform along a periodic axis. On a graded or curved face it is not a physical parabola.

prescribed_flow

Identity. handler: prescribed_flow on an INLET face -> BC_HANDLER_INLET_PROFILE_FROM_FILE -> Create_InletProfileFromFile.

What it does. Reads a per-face velocity profile and imposes it on the inlet. The profile is supplied as a PICSLICE-format field whose dimensions must match the inlet face's cell counts.

When to choose it. When the inlet profile matters and an analytic shape will not do: matching a reference DNS inlet, carrying a developed field in from a precursor run, or driving a duct with a computed Poiseuille solution. This is the only handler that accepts external data.

Parameters it owns. source - required, a mapping whose type selects one of three acquisition routes:

source.type Required keys Optional keys Use when
file path - You already have a PICSLICE profile file
generated generator script, params A shipped generator can produce it (square_duct_poiseuille)
field_slice field_file, grid_file, and one of source_case or velocity_scale script, source_block, slice You are extracting a plane from an existing solution

Unknown keys inside source are rejected with the allowed set named, rather than silently dropped. output_file is refused for both generated and field_slice: picurv writes generated profiles under <run.inputs>/inlet_profiles/.

Interactions. Profile dimensions are validated against the resolved grid block node counts before the run starts: a -Xi/+Xi face expects (KM-1, JM-1), -Eta/+Eta expects (KM-1, IM-1), and -Zeta/+Zeta expects (JM-1, IM-1). A mismatch is a validation error, not a runtime surprise. field_slice needs either source_case (to inherit that case's scaling) or an explicit velocity_scale, because a raw field carries no units of its own.

Diagnostics. The startup banner prints the resolved source_file for the face. Validation reports expected versus actual profile dimensions on mismatch, and the resolved profile file is written into the run's config directory, so what was actually imposed is recoverable after the fact.

Evidence. Unit verified - make unit-boundaries covers the handler in tests/c/test_boundaries.c. Integration verified - tests/test_config_regressions.py exercises all three source types and the face-dimension validation. It is offered by examples/master_template; no shipped runnable example currently selects it, so there is no production-exercised facet. Analytically verified - duct-poiseuille-picard-2026-09-18: a generated square-duct profile on a grid_gen grid delivered the requested flux to 1e-9 and was held downstream; programmatic-inlet-flux-2026-09-18 measured the flux excess on programmatic_c grids that the 2026-09-21 fix below removed, and programmatic-inlet-flux-fixed-2026-09-21 measured the fixed inlet delivering the requested flux to 1e-9 on uniform and stretched programmatic_c grids of 8 and 16 cells across. Unit verified - tests/test_workspace_lifecycle.py checks that a generated profile on a stretched programmatic_c grid is normalized on its face areas and re-identifies when the grid settings change.

Limitations. The profile is steady: the same values are reapplied on every boundary pass, with no time variation. A generated profile is sampled at the target grid's face centres and normalized by face area on every grid mode. On programmatic_c, which the simulator builds itself, the target is the single-block bridge grid written with the solver's own node formula, so a multi-block programmatic_c case with a generated profile is refused at validation. Before 2026-09-21 a programmatic_c profile was sampled at uniform logical points and normalized to its continuous mean, and the inlet delivered roughly 2/n too much flux for n cells across - 12% at 16 cells.

conservation

Identity. handler: conservation on an OUTLET face -> BC_HANDLER_OUTLET_CONSERVATION -> Create_OutletConservation.

What it does. Sets the outlet flux to whatever global mass conservation requires, given everything entering the domain. It is a consequence, not a prescription.

When to choose it. The default and currently only outlet. Because it derives its flux rather than imposing one, it pairs with any inlet without you having to match numbers by hand.

Parameters it owns. None.

Interactions. Outlet has the lowest boundary priority, so inlet and wall win at shared edges. There must be an inlet, or a driven periodic pair, for the outlet to conserve against.

Diagnostics. The inlet/outlet flux balance is reported at startup and is the first thing to check when mass appears not to be conserved.

Evidence. Unit verified - make unit-boundaries. Production exercised in examples/flat_channel. Analytically verified - duct-poiseuille-picard-2026-09-18: outflow matched inflow to 1e-9 in a laminar square duct.

Limitations. BC_HANDLER_OUTLET_PRESSURE, a pressure-specified outlet, exists in the C enum but is not exposed, and no handler implements it; see 6b. Pressure Boundary Conditions for what adding one involves.

geometric

Identity. handler: geometric on a PERIODIC face -> BC_HANDLER_PERIODIC_GEOMETRIC -> Create_PeriodicGeometric.

What it does. Makes a face pair genuinely periodic: the two faces are treated as the same location, with field values and metrics synchronized across the seam. It imposes no forcing - whatever flux the flow has, it keeps.

When to choose it. Any direction that is statistically homogeneous and needs no driving: the spanwise direction of a channel, or a decaying-turbulence box where the flow should decay rather than be sustained. If you need to sustain a flow against wall friction, you want one of the driven handlers instead.

Parameters it owns. None.

Interactions. Periodic axes are derived from paired PERIODIC boundary conditions before DMDA creation, so both faces of the pair must declare it and the grid must satisfy the translation contract. See 2. Grid Contract.

Diagnostics. The startup banner reports the BC-derived periodic axes and each validated translation.

Evidence. Unit verified - make unit-periodic. Regression verified - make smoke-periodic. Production exercised in the decaying-isotropic-turbulence example. Analytically verified - tgv2d-picard-order-2026-09-18 on a triply periodic box, periodic-channel-laminar-picard-2026-09-18 on a doubly periodic channel, and pipe-poiseuille-curvilinear-2026-09-18 across the ends of a curvilinear pipe, each at second order.

Limitations. Requires a matching translation between the paired faces; a grid that is not truly periodic is rejected rather than approximated. The wall-bounded pseudo-time stall once recorded here did not reproduce at current code (5.7 Known limitations).

constant_flux

Identity. handler: constant_flux on a PERIODIC face pair -> BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX -> Create_PeriodicDrivenConstant.

What it does. Periodic in the geometric sense, plus a body force that sustains the flow at a flux you prescribe. Without it a periodic wall-bounded flow decays under wall friction.

When to choose it. When the flux is the quantity you are specifying - matching a literature bulk Reynolds number, for instance. Choose initial_flux instead when the flux you want is already present in the field you are starting from.

Parameters it owns. target_flux (required) and enforce_seam_flux (optional, default false; apply_trim is accepted as a deprecated alias).

Warning
target_flux is a volumetric flux - the bulk velocity U_b times the cross-sectional area - not a velocity. Supplying a velocity here silently produces a flow at the wrong Reynolds number.

Interactions. Both faces of the pair must carry the same driven handler. The target is re-read from the bcs file on every run, so editing it between restart segments takes effect - deliberately unlike initial_flux. enforce_seam_flux enables a second, local actuator at the seam plane; it is off by default because it can reintroduce divergence the pressure solve just removed.

Diagnostics. <run.analysis.metrics>/driven_flow.csv records one row per step: the target and measured volumetric flux, the cross-section area, the bulk velocity, the correction, and driving_acceleration, the force per unit mass the flow received that step (zero on a step whose correction was negligible). Its time average is the mean wall stress balance: u_tau^2 / h for a channel of half-height h. picurv summarize --plot driven_flow.driving_acceleration draws it. Bcs.Uch records the seam correction whether or not it is applied, so you can see whether you need it before enabling it.

Evidence. Regression verified - tests/smoke/run_driven_periodic_regression.sh and make smoke-driven-periodic. Analytically verified - periodic-channel-laminar-picard-2026-09-18: on a laminar channel the applied force matches 3 nu U_b / h^2 to 0.18% at the finest grid, the profile converges at second order, and the bulk velocity converges to the controller law below; explicit-rk4-order-2026-09-21 finds the same law under Explicit RK4.

Limitations and full treatment. The proportional controller settles below its target, at U_target / (1 + 3 nu dt / (2.7 h^2)) on a laminar channel - 10% low at dt = 1, nu = 0.1, h = 1 - so choose dt small against h^2 / nu or set the target above the flux wanted. The control law, update cadence and its consequences for both momentum solvers, the restart contract, and the worked validation cases are documented at 5. Driven Periodic Flows.

initial_flux

Identity. handler: initial_flux on a PERIODIC face pair -> BC_HANDLER_PERIODIC_DRIVEN_INITIAL_FLUX -> Create_PeriodicDrivenInitial.

What it does. The same controller as constant_flux, targeting the flux it measures from the field the run starts with, then holding it.

When to choose it. When you have seeded a field whose flux is already what you want - typically a developed field carried in from a precursor - and you want it preserved rather than restated as a number.

Parameters it owns. enforce_seam_flux only (optional, default false; apply_trim is accepted as a deprecated alias, exactly as for constant_flux). target_flux is rejected, not ignored: accepting it would make the two driven handlers indistinguishable in a case file.

Interactions. Both faces of the pair must carry the same driven handler. The measured target is written to the checkpoint and restored on restart, so a resumed run holds the original target rather than re-measuring a drifted one. Checkpoints written before that metadata existed have no entry; the controller then re-measures and says so in the log.

Diagnostics. The latched target is reported once when measured and again when restored from a checkpoint. A restart that re-measures instead of restoring is logged explicitly. Each step writes the same driven_flow.csv row as constant_flux, with the latched value as target_flux.

Evidence. Regression verified - make smoke-driven-periodic. Production exercised in the driven-channel examples. Analytically verified - periodic-channel-laminar-picard-2026-09-18: gives the same field as constant_flux with the same target, to round-off, and reproduces the laminar profile at second order; pipe-poiseuille-curvilinear-2026-09-18 drives Hagen-Poiseuille flow with it on a curvilinear pipe.

Limitations and full treatment. Shares constant_flux's controller and its shortfall below target (p44_cap_constant_flux). See 5. Driven Periodic Flows.

5. Non-Dimensionalization Before C Input

picurv converts physical BC values to solver-scale values before writing bcs.run:

\[ v^* = \frac{v}{U_{ref}}, \qquad Q^* = \frac{Q}{U_{ref} L_{ref}^{2}}. \]

Specifically:

  • vx/vy/vz/v_max are divided by properties.scaling.velocity_ref,
  • prescribed_flow PICSLICE scalar speeds are divided by properties.scaling.velocity_ref while staging the solver-side .picslice,
  • target_flux is divided by velocity_ref * length_ref^2.

5.1 Prescribed Inlet Profile Files

prescribed_flow uses a face-scoped canonical PICSLICE file. It can be supplied directly with source.type: file, generated analytically with source.type: generated, or sliced from an old ufield*.dat with source.type: field_slice. The Python conductor resolves the face/grid context and delegates dimensional profile writes to generators/profile.gen. Generated and field-sliced sources may set optional source.script to a compatible case-relative or absolute override:

PICSLICE
1
<n1> <n2>
<positive_scalar_speed_0>
<positive_scalar_speed_1>
...

The second line is the frame count. Static prescribed profiles require 1. Values are positive scalar speed magnitudes normal to the inlet surface, not Cartesian velocity components and not contravariant fluxes. The C handler uses the existing inlet face sign and metric conversion to populate Ucont and Ubcs.

Expected dimensions follow the existing inlet face loop order:

  • -Xi/+Xi: (KM - 1, JM - 1), ordered (k, j)
  • -Eta/+Eta: (KM - 1, IM - 1), ordered (k, i)
  • -Zeta/+Zeta: (JM - 1, IM - 1), ordered (j, i)

For a case with 64 x 64 x 1024 cells, the corresponding profile dimensions are 64 x 64 for -Zeta/+Zeta, 1024 x 64 for -Xi/+Xi, and 1024 x 64 for -Eta/+Eta.

Copy-paste file-backed inlet example:

boundary_conditions:
- face: "-Zeta"
type: INLET
handler: prescribed_flow
params:
source:
type: file
path: profiles/inlet.picslice

Each face can reference its own .picslice; picurv stages one solver-scale file per face and writes source_file=... into the run's bcs.run.

Generated profiles currently support analytical square-duct Poiseuille flow:

boundary_conditions:
- face: "-Zeta"
type: INLET
handler: prescribed_flow
params:
source:
type: generated
generator: square_duct_poiseuille
# script: tools/custom_profile.py # optional
params:
bulk_velocity: 1.0
n_terms: 101

The face selects the two transverse dimensions automatically:

  • -Xi/+Xi: (KM - 1, JM - 1), ordered (k, j)
  • -Eta/+Eta: (KM - 1, IM - 1), ordered (k, i)
  • -Zeta/+Zeta: (JM - 1, IM - 1), ordered (j, i)

For grid.mode: grid_gen, picurv run --solve generates the grid first, then reads the generated PICGRID header to size the profile. For grid.mode: file, it reads the source PICGRID header. Generated dimensional profiles and profile.info are written under <run.inputs>/inlet_profiles/, then the staged solver-scale .picslice is referenced from bcs.run.

For square_duct_poiseuille, bulk_velocity is the target inlet bulk speed. picurv evaluates the analytical profile at the target inlet face centers and rescales it using geometric quad face areas so sum(u * area) / sum(area) matches bulk_velocity. The target is the grid file for file, the generated grid for grid_gen, and for programmatic_c the bridge PICGRID built from programmatic_settings with the formula the solver uses, so its stretching is honoured. profile.info records the normalization mode, sampling mode, area-weighted mean before and after normalization, total inlet area, face-area range, and discrete sample mean. Only profile.gen run by hand without --target-grid falls back to continuous-area normalization on uniform logical sample points, and profile.info then says so. The areas are geometric quad areas computed from the target grid coordinates, not the C metric face-area vectors.

Field-sliced profiles reuse an existing Cartesian velocity field as a new inlet:

boundary_conditions:
- face: "-Zeta"
type: INLET
handler: prescribed_flow
params:
source:
type: field_slice
field_file: ../old_run/output/checkpoints/step_000000010000/eulerian/block_0000/Ucat.dat
grid_file: ../old_run/inputs/grid/grid.run
source_case: ../old_run/config/case.yml
slice:
face: "+Zeta"
orientation: opposite
normal_tolerance: 0.99

field_slice reads old Ucat, computes source and target face normals from canonical PICGRID geometry, projects dot(Ucat, n_hat) to a scalar normal speed, and writes a dimensional .sliced.picslice. orientation: opposite means the old outward normal and new inlet outward normal must oppose each other; use orientation: same only when both outward normals should align. The extractor fails if normal compatibility or flow sign checks fail. V1 requires exact source/target slice dimension matches and does not interpolate.

6. Periodicity Consistency Rules

Validator checks:

  1. each axis pair must match periodicity:
    • (-Xi,+Xi), (-Eta,+Eta), (-Zeta,+Zeta) are both periodic or both non-periodic.
  2. a driven periodic handler (constant_flux or initial_flux) must match on both faces of a periodic pair.
  3. incomplete face sets fail validation immediately.

The C side derives periodic axes from those pairs in DeterminePeriodicity, before DMDA creation. There are no independent YAML periodic flags.

For each active axis, ValidatePeriodicGeometry also requires:

  • at least four physical nodes along the periodic direction,
  • pointwise matching opposite surfaces under one nonzero constant Cartesian translation,
  • the same translation at every point on the paired surfaces.

These rules are checked independently for mixed periodic/non-periodic cases. PETSc periodic DMDAs wrap indices; PICurv separately constructs translated coordinate images and synchronizes cell-centered, face-family, and component-staggered Eulerian fields. Particle periodic wrapping is not implemented.

7. C-Side Parsing and Dispatch

Generated bcs.run lines use:

<face> <type> <handler> key=value key=value ...

C ingestion path:

  1. ParseAllBoundaryConditions parses and broadcasts face configs.
  2. StringToBCFace, StringToBCType, StringToBCHandlerType convert tokens.
  3. ValidateBCHandlerForBCType enforces type-handler compatibility.
  4. BoundaryCondition_Create maps handler enums to concrete implementations.

The constant, parabolic, and prescribed-flow inlet handlers share one application path. Each mode evaluates a Cartesian boundary velocity; the common path stores it in Ubcs and sets the face-normal Ucont component from its dot product with the local face-area metric. The current scalar modes obtain that Cartesian vector by aligning their speed with the signed unit metric normal.

boundary_faces is the canonical in-memory representation. Do not restore or maintain duplicate persistent legacy BC arrays in UserCtx. If a future low-level integration genuinely needs a translated view, add a narrow on-demand adapter only when a concrete consumer exists.

Current factory-wired handlers include:

  • wall no-slip,
  • inlet constant velocity,
  • inlet parabolic,
  • inlet prescribed flow from file,
  • outlet conservation,
  • periodic geometric,
  • periodic driven constant flux,
  • periodic driven initial flux.

8. Exposed vs Latent Options

Important contributor note:

  • C enums include additional BC categories and handlers (for example symmetry-family entries),
  • but the current end-to-end user contract intentionally exposes the validated subset listed above.
  • source.type: field_slice is Python-side preprocessing only: it creates the same staged source_file contract used by file-backed and generated profiles.

If you add a new BC mode, update all three layers in one change:

  1. Python validator/writer (picurv_cli/core.py),
  2. C parser/factory/handler implementation,
  3. docs and tests (fixtures + smoke checks).

9. Authoring Examples

Constant-velocity inlet + outlet conservation:

boundary_conditions:
- face: "-Xi"
type: INLET
handler: constant_velocity
params: { vx: 1.5, vy: 0.0, vz: 0.0 }
- face: "+Xi"
type: OUTLET
handler: conservation
- face: "-Eta"
type: WALL
handler: noslip
- face: "+Eta"
type: WALL
handler: noslip
- face: "-Zeta"
type: WALL
handler: noslip
- face: "+Zeta"
type: WALL
handler: noslip

Driven periodic flux on Xi:

boundary_conditions:
- face: "-Xi"
type: PERIODIC
handler: constant_flux
params: { target_flux: 0.02, enforce_seam_flux: true }
- face: "+Xi"
type: PERIODIC
handler: constant_flux
params: { target_flux: 0.02, enforce_seam_flux: true }
- face: "-Eta"
type: WALL
handler: noslip
- face: "+Eta"
type: WALL
handler: noslip
- face: "-Zeta"
type: WALL
handler: noslip
- face: "+Zeta"
type: WALL
handler: noslip

10. Common Failure Modes

  • ‘missing required key 'face’/'type'/'handler': malformed BC entry object. -Duplicate face: same face declared twice in one block. -incomplete ... Missing faces: not all 6 faces provided. -Inconsistent periodicity: one side periodic and opposite side non-periodic. -Periodic geometry validation failed: paired surfaces do not differ by one constant nonzero translation, or the periodic axis is too short. -Unknown params`: extra keys not allowed for selected handler.

11. Related Pages