Config-Driven Builders

The build_* functions in src/Builders/ turn flat, namespaced parameter dictionaries into the numerical objects of the package. They exist so that a wrapper (or a test suite) does not have to re-implement the symbol-to-type mapping for every scheme, tableau, limiter and strategy, and so that this mapping is updated together with the types it names.

The physics side is deliberately not here. PDEs, initial conditions and source terms are built by the wrapper, which fills context[:Equation], [:D], [:M] and the source tuples before calling the builders. In HyperCloud.jl, the runners such as run_direct_simulation call the builders below in this order, and the configuration keys are listed in its Configuration Reference.

using HyperCloudCore

context = Dict{Symbol, Any}(:T => Float64, :D => 1, :M => 1, :Equation => eq,
                            :ExplicitSources => (), :ImplicitSources => ())
context[:WeightConf] = Dict(:name => :exponential, :range => 2.5, :alpha => 1.0)

context[:Domain]  = build_domain(Dict(:domain => :rectangular, :mins => (-1.0,), :maxs => (1.0,), :periodic => true), context)
pg                = build_particle_grid(Dict(:Ns => (200,), :mover => :lagrangian), context)
context[:Flux]    = build_flux(Dict(:name => :Rusanov), context)
context[:Limiter] = build_limiter(Dict(:name => :none), context)
context[:MOOD]    = build_mood(Dict(:criterion => :U2, :strategy => :EPD1, :delta_relax => 1e-4), context)
context[:Scheme]  = build_scheme(Dict(:name => :MUSCL, :order => 3), context)
context[:Tableau] = build_tableau(Dict(:stepper => :RK3), context)
ts                = build_timestepper(context)
adaptivity        = build_adaptivity(Dict(:enabled => true), context)

Every builder is build_x(conf::Dict, context::Dict), where conf holds the keys of one namespace with the prefix stripped (:Scheme_order becomes :order; in HyperCloud, extract_namespace does the stripping). The builder dispatches on Val(conf[:name]) (or the namespace's selector key), so adding a variant means defining one more method:

HyperCloudCore.build_flux(::Val{:HLL}, conf::Dict, context::Dict) = MyHLLFlux()

An unknown name raises an error that names the missing method.

Context keys

The context::Dict{Symbol, Any} carries everything that is already built:

KeySet byRead by
:T (legacy :Type)callerall (via real_type)
:Equation, :D, :Mcaller (PDE builder)grid, schemes, time stepper, domains
:Domainbuild_domainbuild_particle_grid, build_mover
:WeightConfcaller (Weight namespace)build_particle_grid → build_weights
:max_dx, :interp_rangebuild_particle_grid, build_weights
:Gridbuild_particle_gridbuild_mood_criterion, memory reservation
:Flux, :Limiter, :MOOD, :Scheme, :Tableauthe respective buildersbuild_scheme, build_timestepper
:ExplicitSources, :ImplicitSourcescallerbuild_timestepper
:VolumeEstimator (optional)callerbuild_timestepper
:Storage (array type, default Array)callerbuild_particle_grid (device_grid), build_scheme, build_timestepper
:MemoryLimit (bytes), :MemoryTolerance, :SlotsPerParticlecallerbuild_timestepper (reserve!; stores the reservation in :Memory)

:Storage and :MemoryLimit are how a run is put on a GPU and given a memory budget; see Execution backends and the GPU roadmap.

Selectors

BuilderNamespaceSelectorValues
build_domainGrid:domain:rectangular, :spherical
build_weightsWeight:name:exponential, :inverse
build_moverGrid:mover:none, :lagrangian, :scaled_lagrangian, :sinusoidal
build_volume_estimatorGrid:volumes:shepard, :mls
build_fluxFlux:name:Rusanov, :Upwind
build_limiterLimiter:name:none, :minmod, :superbee, :VK, :BJ (+ :mode)
build_moodMOOD:criterion, :strategy:none/:only/:U1/:U2; :EPD0/:SEPD0/:EPD1/:EPD2
build_schemeScheme:name:MUSCL, :Upwind, :Central, :WENO
build_tableauTime:stepper:Euler, :RK2, :RK2Ralston, :RK3, :RK4, :IMEXEuler, :ARS222, :ARS233, :PRSSP3, :SSP332
build_adaptivityAdapt:enabledtrue/false (+ the ParticleAdaptivity knobs)

build_particle_grid calls build_weights, build_mover and build_volume_estimator itself, and build_domain resolves the boundary conditions with parse_bc. The per-builder keys are listed in the docstrings below.

Reference

HyperCloudCore.real_type — Method
real_type(context) -> DataType

The floating-point type of the run: context[:T], or the legacy key context[:Type], else Float64.

source
HyperCloudCore.storage_type — Method
storage_type(context) -> Type

The array type the run lives on: context[:Storage], else Array. With a device array type (e.g. ROCArray) the grid builder returns a device_grid and the scheme and time stepper builders allocate their buffers there (see docs/src/gpu_roadmap.md).

source
HyperCloudCore.build_weights — Function
build_weights(weight_conf, context) -> MLSWeightFunction
build_weights(::Val{name}, weight_conf, context)

Weight namespace. Selector :name: :exponential (ExponentialWeightFunction) or :inverse (InverseWeightFunction), both with the required shape parameter :alpha. :range (required) is the support radius in multiples of context[:max_dx] (set by build_particle_grid, which calls this builder); the absolute radius is stored in context[:interp_range].

Add a weight function with a method build_weights(::Val{:my_weight}, weight_conf, context).

source
HyperCloudCore.build_domain — Function
build_domain(domain_conf, context) -> GeometricDomain
build_domain(::Val{name}, domain_conf, context)

Grid namespace. Selector :domain (a Symbol or String):

Both read :periodic (Bool or NTuple{D, Bool}, default false) and the boundary conditions (:bc, see parse_bc). A GeometricDomain among the values of domain_conf is returned as it is, so a pre-built domain can be passed through the configuration.

source
HyperCloudCore.parse_bc — Function
parse_bc(domain_conf) -> Dict{Int, AbstractBoundaryCondition}

The bc_map of a domain from domain_conf[:bc] (or :bc_map): a dictionary tag => bc, where bc is an AbstractBoundaryCondition instance, :outflow/:OutflowBC, :fixed_dirichlet/:FixedDirichlet, or the name of a zero-argument constructor defined in HyperCloudCore or in Main. A string is parsed and evaluated as Julia code first. Without the key the map is empty.

source
HyperCloudCore.SinusoidalVelocity — Type
SinusoidalVelocity{D, T}(A, mins, Ls)

w_d(x) = A sin(2π (x_d - mins_d) / Ls_d): the velocity field of the :sinusoidal mover. A bits-type callable (a closure capturing T or D as values would not be isbits and could not enter a GPU kernel).

source
HyperCloudCore.build_mover — Function
build_mover(grid_conf, context) -> GridMover
build_mover(::Val{name}, grid_conf, context)

Grid namespace. Selector :mover (default :none):

:moverMoverKeys
:noneNoGridMover
:lagrangianLagrangianMover
:scaled_lagrangianScaledLagrangianMover:mover_alpha
:sinusoidalPrescribedMover with SinusoidalVelocity:mover_amplitude (needs context[:Domain])

The :sinusoidal velocity, $w_d(x) = A \sin(2\pi (x_d - \mathrm{min}_d) / L_d)$, is periodic on the domain. Called by build_particle_grid.

source
HyperCloudCore.build_particle_grid — Function
build_particle_grid(grid_conf, context) -> ParticleGrid

Grid namespace. Requires real_type(context), [:D], [:M], [:Domain] and [:WeightConf] (the Weight namespace).

KeyMeaning
:NsParticles per direction (tuple, required); the lattice spacing is (maxs - mins) ./ Ns
:randomness_factorJitter per direction as a fraction of the spacing (default 0)
:seed (legacy :SEED)Seed of the MersenneTwister for the jitter (default 42)
:mover, :mover_*Grid mover, see build_mover
:volumes, :volumes_*Volume estimator, see build_volume_estimator

Stores the largest spacing in context[:max_dx], builds the weight function with build_weights(context[:WeightConf], context) and sets context[:Grid]. The grid is built on the host and moved to storage_type(context) with device_grid if that is not Array.

source
HyperCloudCore.build_limiter — Function
build_limiter(limiter_conf, context) -> AbstractSlopeLimiter
build_limiter(::Val{name}, limiter_conf, context)

Limiter namespace. Selector :name (default :none): :none (NoLimiter()), :minmod, :superbee (1-D only), :VK (Venkatakrishnan) or :BJ (Barth–Jespersen). Every limiter except :none requires :mode (:hard limits all reconstruction coefficients, :soft only the first-order ones). See MUSCL Reconstruction and Limiting.

source
HyperCloudCore.build_scheme — Function
build_scheme(scheme_conf, context) -> DivergenceInterpolator
build_scheme(::Val{name}, scheme_conf, context)

Scheme namespace. Requires real_type(context), [:D] and [:M]. Selector :name:

:nameSchemeAlso reads
:MUSCLMUSCLcontext[:Flux], context[:Limiter]; allocates on storage_type(context)
:UpwindUpwindDivergencecontext[:Flux]; :upwind_alg_nd (:Classic, :Tiwari or :Praveen)
:CentralCentralDivergence
:WENOWENO

All take the consistency order :order (required) and the divergence order :MLS_order (the div_order keyword, default 0: follow the particle's order).

Add a scheme with a method build_scheme(::Val{:my_scheme}, scheme_conf, context) returning a DivergenceInterpolator (see API Contracts).

source
HyperCloudCore.build_tableau — Function
build_tableau(time_conf, context) -> IMEXButcherTableau

Selector :stepper: :Euler, :RK2 (SSP), :RK2Ralston, :RK3 (SSP), :RK4, :ARS222, :ARS233, :PRSSP3, :SSP332, :IMEXEuler. Explicit tableaus are returned as IMEXButcherTableaus with a zero implicit part.

source
HyperCloudCore.build_timestepper — Function
build_timestepper(context; time_conf = Dict()) -> GeneralRKTimeStepper

Requires context[:Equation], [:Scheme], [:MOOD], [:Tableau], [:ExplicitSources], [:ImplicitSources]. Optional Time keys: reset_orders_per_stage, full_search_per_stage; the volume estimator comes from context[:VolumeEstimator] if set (otherwise the stepper default), the stage buffers live on storage_type(context). With context[:MemoryLimit] (bytes) the buffers of the grid, the scheme and the stepper are reserved under that limit (reserve!, with the optional context[:MemoryTolerance] and context[:SlotsPerParticle]); the reservation is stored in context[:Memory].

source