Domain¶
When configuring a simulation, one of the first things required is to define the domain. What is considered here by domain is:
Domain size for the simulation
Bodies in this volume
Refinements to apply in the volume
Domain size¶
The domain size for the simulation is the volume in which the simulation will run.
simulations:
- name: example
domain:
# All dimensions in domain_size MUST BE divisible by block_size
domain_size:
x: 96
y: 80
z: 128
# Rescale domain for output
# pos_rescaled = pos * scale + translation
domain_rescale:
translation: [0, 0, 0]
scale: [1, 1, 1]
# Leave it as 8
block_size: 8
Note
The simulation volume is a box (2D or 3D).
The domain is divided in blocks that are boxes with size block_size.
These blocks can be refined recursively, which generate nodes that have half the distance between them.
The higher the level, the more refined it is, with level 0 being the domain with no refinement.
Blocks communicate with each other through neighbours on the same level, or with their parent/children of different level.
Important
All dimensions in the domain_size must be divisible by block_size.
For the block size, there is a limitation between it and the shared memory size of the GPU. It’s recommended to leave it as 8, because most GPUs will be able to run the simulation with this size.
To write exported coordinates in a physical frame (metres, a site origin, …) instead of lattice
units, define a coordinate system and select it per export through
data.exports.<name>.system (see Export coordinate system).
Sizing by physical extent¶
Instead of a node count, the level-0 domain can be sized by a physical extent given in a coordinate system, through domain_extent (and domain_extent_system, defaulting to domain.default_system).
The extent is scaled to lattice nodes through the system, and each axis is rounded up to a block_size multiple (a warning reports the rounding), so the realized domain is at least the requested extent.
The realized physical extent and the resulting lattice size are written to info.yaml under domain_extent.
domain:
block_size: 8
systems:
site:
scale: 4.0 # 1 site unit (e.g. metre) -> 4 lattice nodes at level 0
domain_extent: [300.0, 200.0, 150.0] # in `site`; -> 1200 x 800 x 600 nodes
domain_extent_system: site
Provide either domain_size or domain_extent. A domain_size supplied alongside domain_extent is accepted only when it matches the derived node count (so a dumped config, which carries both, re-loads cleanly); an inconsistent pair is a configuration error. The extent system must not be rotated (the grid is axis-aligned).
Bodies¶
For the bodies inside the domain the field domain.bodies is used
simulations:
- name: example
domain:
bodies:
# Body named CAARC
CAARC:
# IBM configurations
IBM:
# True to run this body
# Defaults to true
run: True
# IBM configuration name, at `models.IBM.cfgs.{cfg_name}`
# Defaults to "default"
cfg_use: cfg_name
# IBM order for forces interpolation spread
# Bodies in same order have forces calculated at the same time (don't affect each other)
# Forces are computed from smaller to bigger order, similar to how BC is done
# Defaults to 0
order: 0
# Interval in time steps in which to run IBM
# When end step or start step is zero, they're not applied
# Defaults to {end_step: 0, start_step: 0} (full simulation)
interval_run:
end_step: 0
start_step: 0
# Path to .lnas file for geometry
geometry_path: fixture/lnas/wind_tunnel/CAARC.lnas
# The min max area for the triangles to use (considering lvl 0)
# bigger triangles are refined until reach the level area
# smaller triangles are treated as `small_triangles`
# `max` should be at least 4 times`the `min` size
# Defaults to { min: 0.25, max: 1 }
area: { min: 0.25, max: 1 }
# Height to assume for surface, for volumetric algorithms.
# Defaults to 1
height_assume: 1
# Whether the surface height scales per level or not. The scaling is:
# height_lvl = height_assume * 2^lvl
# Defaults to false
height_scales_per_lvl: false
# Treatment triangles for small triangles
# add: adds the small triangles
# ignore: ignore the small triangles
# Defaults to error
small_triangles: "add" # | "ignore"
# Add copy of this geometry in given normal offsets.
# Defaults to [] (empty list)
normal_offsets_add: !range [-5, 1, 1]
# Volumes limit, remove triangles outside it in each geometry step
# Defaults to no limit (no triangle is removed)
volumes_limits:
# These volumes are before any transformation in the body
raw:
- start: [10, 20, 0]
end: [100, 100, 100]
# These are applied after the body's transformations
body_transformed:
- start: [60, 40, 0]
end: [100, 200, 100]
# These are applied after all transformations are applied
full_transformed:
- start: [60, 40, 0]
end: [100, 200, 100]
- start: [100, 40, 50]
end: [150, 140, 70]
# Transformations to apply in the body
# For more information check the `transformations` section
# Defaults to no transformation
transformation:
fixed_point: [0, 0, 0]
# Rotation is in radians
rotation: [0, 0, 0]
scale: [1.0, 1.0, 1.0]
translation: [12.0, 0.0, 1.0]
# Another body, the only required field is geometry_path, all others are optional
cube:
geometry_path: fixture/stl/basic/cube.stl
# Domain limits for effect of bodies in the domain
# (i.e. There are no IBM nodes outside this volume)
bodies_domain_limits:
start: [8.0, 4.0, 0.0]
end: [448.0, 156.0, 48.0]
# Defaults to true
is_abs: true
Important
The body uses a .lnas file for the geometry specification.
The geometry consists of a series of triangles and vertices, which are used and processed in multiple ways.
The file also have a list of surfaces that are defined by a series of triangles in the geometry. This surfaces may be used for multiple purposes as well.
Point Cloud¶
For specification of point clouds the field domain.point_cloud is used
simulations:
- name: example
domain:
point_cloud:
# Point cloud named TreeCloud
TreeCloud:
# IBM configurations, same from bodies, to specify IBM to run
IBM:
run: True
cfg_use: cfg_name
order: 0
interval_run:
end_step: 0
start_step: 0
# Path to .csv file for point cloud
# Must have keys: [x, y, z, nx, ny, nz, area]
csv_path: fixture/point_cloud/tree.csv
# Transformation to apply in the point cloud
# For more information check the `transformations` section
# Defaults to no transformation
transformation:
fixed_point: [0, 0, 0]
# Rotation is in radians
rotation: [0, 0, 0]
scale: [1.0, 1.0, 1.0]
translation: [12.0, 0.0, 1.0]
# Another body, the only required field is geometry_path, all others are optional
another_point_cloud:
csv_path: fixture/point_cloud/cube.csv
Transformations¶
It’s possible to apply transformations to the bodies. The operations that we support are:
Scaling
Rotation
Translation
These are all linear transformations that are applied sequentially. There is also an important aspect which is the fixed point, that defined the point in which the geometry is scaled and rotated in relation to.
The full order of transformations is:
Translate body to centralize at (0, 0, 0) the fixed point
Apply scale transformation
Apply rotation transformation
Apply translation transformation
Revert the translation done in step 1
For more informations and specifications on how this is done, check the LNAS repository.
Coordinate systems¶
Positioned inputs (bodies, point clouds and refinement volumes) are given in lattice coordinates by default, the base frame Nassu calls lbm.
When your geometry and reference data live in a physical frame - metres on a site, a building-local axis, a wind-tunnel origin - it is convenient to express those coordinates in that frame and let the solver convert them.
The domain.systems registry declares named frames for exactly this, and the config layer composes each one down to lbm at setup.
simulations:
- name: example
domain:
systems:
# A site frame in metres: 1 m -> 4 lattice nodes, origin shifted in.
# `parent` defaults to "lbm".
site:
scale: 4.0
translation: [16.0, 8.0, 0.0]
# A building frame chained onto `site` (parent: site -> lbm).
building:
parent: site
scale: 1.0
rotation: [0, 0, 0.7853981634] # 45 deg yaw about z
translation: [10.0, 6.0, 0.0]
fixed_point: [0, 0, 0]
# Frame used by positioned objects that do not set their own `system`.
default_system: lbm
Each entry is a similarity transform relative to its parent:
scale- a single isotropic positive scalar. The lattice is cubic, so per-axis scale is rejected; use a body’s owntransformationif you genuinely need anisotropic scaling.rotation- a 3-tuple of angles in radians.translation- a 3-tuple.fixed_point- the point about which the scale and rotation are applied.
The transform order is the same as the transformation field above: scale, then rotate, then translate, all about fixed_point.
A system’s parent is the frame it is expressed in (default lbm).
Systems chain through their parents, so a building frame whose parent is site whose parent is lbm resolves the whole way down to the lattice automatically.
The lbm name is reserved - it is the base lattice frame (the identity) and cannot be redefined under domain.systems.
Tag a positioned object with the frame its coordinates are given in through the system field, available on bodies (domain.bodies.*), point clouds (domain.point_clouds.*), refinement volumes, the refinement limit and exclude volumes (volume_refinement_limit, volumes_not_refine), volume export targets (data.exports.*.target.volumes.*.volume), historic-series probes (data lines, points, planes and CSVs) and field-monitor volumes.
A system of null (or omitting the field) uses domain.default_system.
bodies:
CAARC:
geometry_path: fixture/lnas/wind_tunnel/CAARC.lnas
# Coordinates given in the metre `site` frame.
system: site
Note
A body or point cloud’s own transformation is applied first, then the resolved system->lbm map. The system only changes the frame the input is interpreted in; it does not replace the per-object transform.
A config that declares no systems keeps everything in lbm, which is the identity.
Refinement volumes¶
Refinement volumes_refine accept a system with two restrictions:
Only absolute volumes (
is_abs: true) may use a non-lbmsystem. A relative (is_abs: false) volume with a non-lbmsystem is a configuration error.The system must not contain a rotation. Rotated or oriented refinement volumes are not supported yet (the octree refinement keeps axis-aligned boxes), so a rotated system on a volume is a configuration error.
refinement:
static:
volume_refine:
volumes_refine:
- lvl: 1
is_abs: true
start: [0.0, 0.0, 0.0]
end: [32.0, 160.0, 64.0]
system: site # metre frame, no rotation, is_abs: true
Refinement limit, exclude and volume export targets¶
The remaining volume boxes - the refinement limit (volume_refinement_limit), the exclude volumes (volumes_not_refine), and volume export targets (data.exports.*.target.volumes.*.volume) - accept the same system tag. These slots carry no orientation (they only gate which blocks are refined, or which region is written), so only absolute (is_abs: true), non-rotated systems are accepted; a rotated system is a configuration error. The corners are mapped to lattice coordinates at load, so downstream handling is unchanged.
refinement:
static:
site_frame:
volumes_refine: [{lvl: 1, is_abs: true, start: [0, 0, 0], end: [32, 160, 64]}]
volume_refinement_limit:
is_abs: true
start: [0.0, 10.0, 0.0]
end: [40.0, 50.0, 150.0]
system: site # metre frame, non-rotated, is_abs: true
volumes_not_refine:
- {is_abs: true, start: [0, 10, 0], end: [40, 50, 150], system: site}
For a volume export target the box’s system is the input frame the corners are read in; the frame the exported coordinates are written in is the export’s own system (see Export coordinate system).
data:
exports:
wake:
target:
volumes:
near_building:
volume:
start: [0.0, 0.0, 0.0]
end: [120.0, 80.0, 40.0]
is_abs: true
system: site # corners given in the metre frame
Probes and monitors¶
The point-like historic-series probes (data lines, points, planes and CSVs) and the field-monitor volumes (data.monitors.fields.*.volumes_monitor / volumes_ignore) also accept a system, so probe and monitor coordinates can be given in the same physical frame as the geometry they sample.
data:
exports:
site_line:
target:
lines:
along_street:
start_pos: [0.0, 0.0, 2.0]
end_pos: [120.0, 0.0, 2.0]
dist: 2.0
system: site # endpoints given in the metre frame
monitors:
fields:
near_building:
volumes_monitor:
- start: [0.0, 0.0, 0.0]
end: [40.0, 40.0, 20.0]
is_abs: true
system: site
Probe points are mapped from their system into lbm before any probe transformation. Monitor volumes follow the same axis-aligned rule as refinement volumes: only absolute (is_abs: true), non-rotated systems are accepted (express orientation through the probe transformation mechanism instead).
Note
Coordinate systems span input positioning (above), domain sizing (domain_extent), and the export side (writing output coordinates in a named frame, via an export’s system).
Domain Refinement¶
For the domain refinement specification, the domain.refinement field is used
Each entry under refinement.static is an arbitrary user-chosen name that groups one RefinementStaticConfigs block. A single entry can contain volumes_refine, bodies, volume_refinement_limit, and volumes_not_refine in any combination. The names volume_refine, body_refine, and others shown below are examples - you can use any names that are meaningful for your simulation.
simulations:
- name: example
domain:
refinement:
static:
# User-defined name for this refinement group - refines a volume
volume_refine:
# List of volumes to refine
volumes_refine:
# The volume specification and level to refine.
# It's possible to use a relative volume by specifying is_abs: False
# By default is_abs defauls to true
# All blocks inside these volumes are refined
- lvl: 1
start: [0.0, 0.0, 0.0]
end: [32.0, 160.0, 64.0]
# Defaults to true
is_abs: true
# User-defined name for this refinement group - refines around a body
body_refine:
bodies:
# Bodies to refine
- body_name: CAARC
# Level to use
lvl: 4
# Normals offset to refine
# This offsets the vertices in normal direction multiplying by the offset
# and refines this body. All blocks that the body is in are refined
normal_offsets: !range [-0.5, 2.1, 0.5]
# It's possible to specify only a surface of the body for usage too
- body_name: CAARC.surface_name
lvl: 4
normal_offsets: !math [1/4]
# Transformation to apply to body for transformation
transformation:
translation: [0.0, 0.0, -1.0]
fixed_point: [0, 0, 0]
rotation: [0, 0, 0]
scale: [1.0, 1.0, 1.0]
# Limiting the volume for refinement
limit_volume:
bodies:
- body_name: CAARC
lvl: 6
normal_offsets: [0, 1]
# This limits the volume that can be refined by all refinemets (volume and body)
volume_refinement_limit:
start: [0, 10, 0]
end: [40, 50, 150]
# Defaults to true
is_abs: true
# Ignoring volumes
ignore_volumes:
bodies:
- body_name: CAARC
lvl: 6
normal_offsets: !range [-0.5, 5, 0.2]
# List of volumes to not refine.
# If a block is fully inside any of these volumes it's not refined
volumes_not_refine:
- start: [0, 10, 0]
end: [40, 50, 150]
# Defaults to true
is_abs: true
- start: [0, 0.15, 0.1]
end: [0.1, 0.2, 0.3]
# Example using all cases presented above
full_example:
volumes_refine:
- is_abs: true
lvl: 1
start: [0.0, 0.0, 0.0]
end: [32.0, 160.0, 64.0]
- is_abs: true
lvl: 4
start: [10, 20, 30]
end: [32.0, 160.0, 64.0]
bodies:
- body_name: CAARC
lvl: 6
normal_offsets: [-0.1, 0, 0.1]
volume_refinement_limit:
is_abs: true
start: [0, 10, 0]
end: [40, 50, 150]
volumes_not_refine:
- is_abs: true
start: [0, 10, 0]
end: [40, 50, 150]
- is_abs: false
start: [0, 0.15, 0.1]
end: [0.1, 0.2, 0.3]
Important
The refinement follows some rules, one important is that blocks that “communicate” with each other are at most one level difference. So when specifying a volume to refine up to level 2, the blocks in the limit of this volume will be at level 1.
Global transformations¶
When considering a change of scale, rotation of domain or change of perspective, it’s common to apply these in multiple geometries and entities in the domain.
For this we provide the field global_transformations, that allows the user to apply the same transformation to multiple entities.
global_transformations:
# List of transformations to apply
- transformation:
translation: [0, 10, 3.6]
point_clouds_apply: ["tree", "one_sponge"]
bodies_apply: ["CAARC"]
probes_apply:
[
"series.series1.lines",
"series.series1.points.point1",
"series.another_series",
]
# True to apply this transformation to bodies in series as well
apply_to_bodies_probes: false
# Another transformation
- transformation:
fixed_point: [608.90625, 64, 0]
rotation: [0, 0, 3.141592654]
scale: [1, 1, 1]
point_clouds_apply: []
bodies_apply: ["CAARC"]
probes_apply: []
It’s possible to define a list of transformations to apply for a list of bodies and probes. These are applied after the body specific ones and in sequence to each other. So in the example above, first the translation is applied to the body and the probes and only after that the body is rotated. The same rules serves for the probes.
Note
To know more about probes, check their section in data.
For the probes, it’s possible to select a series of probes in it.
Say the transformation must be applied to all lines in the series called series1.
We can specify it using the series.series1.lines.
But if we want to apply it only to a single line called my_line, we’re able to do it using series.series1.lines.my_line.
Same applies to transform all geometries in a series or other fields such as bodies, points or csvs.