Flow Over Mounted Cube

The simulation of a wall-mounted cube is used for the validation of the combined features usage. The multiblock refined for a IBM body is used, the synthetic eddy method is applied at inlet to reproduce a category II velocity profile from EU standard. The turbulent flow around a surface-mounted cube is well estabilished in literature with results of experimental data available.

We seek to reproduce correct values of the average and standard deviation of the pressure in the cube surface.

[1]:
from nassu.cfg.model import ConfigScheme

filename = "./tests/validation/cases/08_flow_over_mounted_cube.nassu.yaml"

sim_cfgs = ConfigScheme.sim_cfgs_from_file_dct(filename)

The lateral boundaries and top of domain use free slip boundary conditions, for the outlet, a Neumann BC with fixed pressure is adopted.

Results

The results of the pressure coefficient are compared to the average of different laboratory results. An excellent agreement was achieved. The root mean squared pressure coefficent also presented great accordance with experimental data.

[2]:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import pathlib
from tests.validation.notebooks import common

common.use_style()


def get_experimental_profile_Cp(reynolds: float) -> dict[str, dict[str, pd.DataFrame]]:
    files_tau: dict[float, dict[str, dict[str, str]]] = {
        20000: {
            "frontal": {
                "Lim": "Wall_mounted_cube/Re_20000/pressure_coefficient_frontal_lim2009.csv",
                "Holscher": "Wall_mounted_cube/Re_20000/pressure_coefficient_frontal_holscher1998.csv",
            },
            "side": {
                "Lim": "Wall_mounted_cube/Re_20000/pressure_coefficient_side_lim2009.csv",
            },
            "rms": {
                "Holscher": "Wall_mounted_cube/Re_20000/pressure_coefficient_rms_top_lim2009.csv",
            },
            "skew": {
                "Holscher": "Wall_mounted_cube/Re_20000/pressure_coefficient_skew_top_lim2009.csv",
            },
        }
    }
    files_get = files_tau[reynolds]
    vals_exp: dict[str, dict[str, pd.DataFrame]] = {}
    for type_file, dct in files_get.items():
        vals_exp[type_file] = {}
        for name, f in dct.items():
            filename = pathlib.Path("tests/validation/comparison") / f
            # ([pos], [Cp])
            df = pd.read_csv(filename, delimiter=",")
            vals_exp[type_file][name] = df
    return vals_exp


exp = get_experimental_profile_Cp(20000)
holscher_frontal = exp["frontal"]["Holscher"]
holscher_top_rms = exp["rms"]["Holscher"]
holscher_top_skew = exp["skew"]["Holscher"]
lim_side = exp["side"]["Lim"]

Calculation of reference velocity:

[3]:
sim_cfgs.keys()
[3]:
dict_keys([('cubeFlowMultilevelLES', 0), ('cubeFlowMultiLevelLESnoCube', 0)])
[4]:
sim_cfg = sim_cfgs["cubeFlowMultiLevelLESnoCube", 0]
line_ref = sim_cfg.output.series["velocity"].points["pitot"]
df_ref = line_ref.read_full_data("ux")

df_ref = df_ref[df_ref["time_step"] > 10000]

ux_avg = df_ref.mean()

u_ref = ux_avg[1]
u_ref
/tmp/ipykernel_1558770/689290571.py:9: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`
  u_ref = ux_avg[1]
[4]:
np.float32(0.050575547)
[5]:
import scipy
from scipy.ndimage import gaussian_filter
from scipy.stats import skew
[6]:
sim_cfg = sim_cfgs["cubeFlowMultilevelLES", 0]


def get_cp_line(line_names: list[str]) -> pd.DataFrame:
    line_series = sim_cfg.output.series["pressure"].lines
    pressure_series = sim_cfg.output.series["pressure"].points["pressure_point"]
    df_pref = pressure_series.read_full_data("rho")
    df_pref = df_pref[df_pref["time_step"] > 10000]
    rho_ref = df_pref["0"].to_numpy().T

    df_ret = pd.DataFrame({})
    for i, line in enumerate(line_names):
        df_hs = line_series[line].read_full_data("rho")
        df_hs = df_hs[df_hs["time_step"] > 10000]
        df_rho = df_hs.drop(columns=["time_step"])
        df_rho["rho_ref"] = rho_ref
        df_cp = (df_rho.subtract(df_rho["rho_ref"], axis=0)) / (1.5 * (u_ref**2))
        df_cp = df_cp.drop(columns=["rho_ref"])
        cp_avg = df_cp.mean().to_numpy().T
        cp_rms = df_cp.std().to_numpy().T
        cp_skew = skew(df_cp).T
        df_cp = pd.DataFrame({"cp_avg": cp_avg, "cp_rms": cp_rms, "cp_skew": cp_skew})
        df_cp["pos"] = np.linspace(i, i + 1, num=len(cp_avg), endpoint=True)
        df_ret = pd.concat([df_ret, df_cp])
    return df_ret


lines_cp_frontal = [f"long_line{i}" for i in range(1, 4)]
df_cp_frontal = get_cp_line(lines_cp_frontal)
lines_cp_side = [f"tr_line{i}" for i in range(1, 4)]
df_cp_side = get_cp_line(lines_cp_side)
[7]:
fig, ax = common.fig_triple()

ax[0].plot(
    holscher_frontal["x/h"],
    holscher_frontal["Cp"],
    **common.markers.exp(shape="o"),
    label="Holscher (1998)",
)
ax[0].plot(
    df_cp_frontal["pos"],
    df_cp_frontal["cp_avg"],
    **common.markers.sim_line(linestyle="-"),
    label="AeroSim",
)
ax[0].set_ylim(-1.5, 1.5)
ax[0].set_ylabel(r"$C_{p,\mathrm{avg}}$")
ax[0].set_xlabel(r"$x/L$")
ax[0].legend()

n_points = len(df_cp_frontal["pos"])
df_top = df_cp_frontal[(df_cp_frontal["pos"] >= 1.0) & (df_cp_frontal["pos"] <= 2.0)]
ax[1].plot(
    holscher_top_rms["x/h"],
    holscher_top_rms["Cp"],
    **common.markers.exp(shape="o"),
    label="Lim (2009)",
)
ax[1].plot(
    df_top["pos"], df_top["cp_rms"], **common.markers.sim_line(linestyle="-"), label="AeroSim"
)
ax[1].set_ylabel(r"$C_{p,\mathrm{rms}}$")
ax[1].set_xlabel(r"$x/L$")
ax[1].legend()
ax[1].set_ylim(0, 0.6)
ax[1].set_xlim(1, 2)

ax[2].plot(
    holscher_top_rms["x/h"],
    holscher_top_skew["Cp"],
    **common.markers.exp(shape="o"),
    label="Lim (2009)",
)
ax[2].plot(
    df_top["pos"], df_top["cp_skew"], **common.markers.sim_line(linestyle="-"), label="AeroSim"
)
ax[2].set_ylabel(r"$C_{p,\mathrm{skew}}$")
ax[2].set_xlabel(r"$x/L$")
ax[2].legend()
ax[2].set_ylim(-1.5, 1)
ax[2].set_xlim(1, 2)

plt.tight_layout()
plt.show(fig)
../../../_images/validation_cases_08_turb_flow_over_wall_mounted_cube_08.1_flow_over_wall_mounted_cube_10_0.png

Good accuracy of results were obtained against experimental data of the average and root mean squared pressure coefficient towards longitudinal direction. The results from Holscher, 1998 consist of an average of multiple wind tunnel experiments of a turbulent flow over a surface-mounted cube.

[8]:
fig, ax = common.fig_single()

ax.plot(lim_side["x/h"], lim_side["Cp"], **common.markers.exp(shape="o"), label="Lim (2009)")
ax.plot(
    df_cp_side["pos"],
    df_cp_side["cp_avg"],
    **common.markers.sim_line(linestyle="-"),
    label="AeroSim",
)

ax.set_ylabel(r"$C_{p,\mathrm{avg}}$")
ax.set_xlabel(r"$x/L$")
ax.set_ylim(-1.5, 1)

ax.legend()

plt.tight_layout()
plt.show(fig)
../../../_images/validation_cases_08_turb_flow_over_wall_mounted_cube_08.1_flow_over_wall_mounted_cube_12_0.png

Good agreement was also observed along the transversal perimeter of the surface-mounted cube.

Power Spectral Density

The power spectral density is also computed in order to check if there are anomalies in the velocity fluctuations.

[9]:
import pandas as pd
[10]:
sim_cfg = sim_cfgs["cubeFlowMultilevelLES", 0]


def read_df_spectrum(point_name: str):
    spectrum_series = sim_cfg.output.spectrum[point_name]
    df = pd.read_hdf(spectrum_series.data_filename)
    df = df[df["time_step"] >= 10000]
    return df


df_sa_pressure = read_df_spectrum("point_pressure_top")
df_sa_velocity_top = df_sa_pressure
df_sa_velocity_wake = read_df_spectrum("point_velocity_wake")
[11]:
Lu = 2.8
f = 1

rho_top = df_sa_pressure["rho"].tolist()
rho_top = np.array(rho_top, dtype=np.float32)

xf_1, yf_img = scipy.signal.periodogram(rho_top, 32, scaling="density")
avg = np.average(rho_top)
st = np.std(rho_top)
yf_img = yf_img * (u_ref / Lu) / (st * st)
xf_1 = xf_1 * (Lu / u_ref)

yf_1 = np.real(yf_img)

ux_top = df_sa_velocity_top["ux"].tolist()
ux_top = np.array(ux_top, dtype=np.float32)

xf_2, yf_img = scipy.signal.periodogram(ux_top, f, scaling="density")
avg = np.average(ux_top)
st = np.std(ux_top)
yf_img = yf_img * (u_ref / Lu) / (st * st)
xf_2 = xf_2 * (Lu / u_ref)

yf_2 = np.real(yf_img)

ux_wake = df_sa_velocity_wake["ux"].tolist()
ux_wake = np.array(ux_wake, dtype=np.float32)

xf_3, yf_img = scipy.signal.periodogram(ux_wake, f, scaling="density")
avg = np.average(ux_wake)
st = np.std(ux_wake)
yf_img = yf_img * (u_ref / Lu) / (st * st)
xf_3 = xf_3 * (Lu / u_ref)

yf_3 = np.real(yf_img)
[12]:
def filter_avg_data(data):
    data = gaussian_filter(data, sigma=7)
    return data


yf_avg_1 = filter_avg_data(yf_1)
yf_avg_2 = filter_avg_data(yf_2)
yf_avg_3 = filter_avg_data(yf_3)
[13]:
fig, ax = common.fig_triple()

ax[0].set_title("Density top")
ax[0].plot(xf_1, yf_1, color=common.colors.sim, alpha=0.7, label="AeroSim")
ax[0].set_ylabel(r"$f\,S_{uu}/ \sigma_{u}^{2}$")
ax[0].set_xlabel(r"$f\,L_{u}/u$")
ax[0].set_xscale("log")
ax[0].set_yscale("log")
ax[0].legend()

ax[1].set_title("Velocity top")
ax[1].plot(xf_2, yf_2, color=common.colors.sim, alpha=0.7, label="AeroSim")
ax[1].set_xlabel(r"$f\,L_{u}/u$")
ax[1].set_xscale("log")
ax[1].set_yscale("log")
ax[1].legend()

ax[2].set_title("Velocity wake")
ax[2].plot(xf_3, yf_3, color=common.colors.sim, alpha=0.7, label="AeroSim")
ax[2].set_xlabel(r"$f\,L_{u}/u$")
ax[2].set_xscale("log")
ax[2].set_yscale("log")
ax[2].legend()

plt.tight_layout()
plt.show(fig)
../../../_images/validation_cases_08_turb_flow_over_wall_mounted_cube_08.1_flow_over_wall_mounted_cube_20_0.png

Version

[14]:
sim_info = sim_cfg.output.read_info()

nassu_commit = sim_info["commit"]
nassu_version = sim_info["version"]
print("Version:", nassu_version)
print("Commit hash:", nassu_commit)
Version: 1.6.45
Commit hash: a2287d74877f61304fc618ad5e2c77a25b3f7f39

Configuration

[15]:
from IPython.display import Code

Code(filename=filename)
[15]:
variables:
  simul:
    dev_time: 10000
    stats_time: 30000
    scale: !math 1/16
    plane_height: 5.01
    sigma_sem: 20
    body_pos: 150
  domain:
    length: 600
    width: 160
    height: 96
  var:
    cube_length: 2.8
    body_lvl: 5
  aux:
    offset: !math 2*(1/(2**${var.body_lvl}))
    ref_delta: 0.02

simulations:
  - name: cubeFlowMultilevelLES
    save_path: ./tests/validation/results/08_flow_over_mounted_cube/

    n_steps: !math "${simul.dev_time}+${simul.stats_time}"

    report:
      frequency: 1000

    data:
      divergence: { frequency: 1 }
      monitors:
        fields:
          macrs_stats:
            macrs: [rho, u]
            stats: [min, max, mean]
            interval: { start_step: 0, frequency: 10 }
      instantaneous:
        full_domain: { interval: { frequency: 0 }, macrs: [rho, u] }
      export_IBM_nodes:
        ibm_exp:
          body_name: cube
          frequency: 5000
      probes:
        historic_series:
          pressure:
            macrs: ["rho"]
            interval: { frequency: 1, lvl: 0 }
            points:
              pressure_point:
                pos:
                  !math [
                    "${simul.body_pos}",
                    "0.5*${domain.width}",
                    "0.5*${domain.height}-32",
                  ]
            lines:
              long_line1:
                dist: 0.0625
                start_pos:
                  !math [
                    "${simul.body_pos} - 0.5*${var.cube_length} - ${aux.offset}",
                    "0.5*${domain.width}",
                    "${simul.plane_height}",
                  ]
                end_pos:
                  !math [
                    "${simul.body_pos} - 0.5*${var.cube_length} - ${aux.offset}",
                    "0.5*${domain.width}",
                    "${simul.plane_height} + ${var.cube_length}",
                  ]
              long_line2:
                dist: 0.0625
                start_pos:
                  !math [
                    "${simul.body_pos} - 0.5*${var.cube_length}",
                    "0.5*${domain.width}",
                    "${simul.plane_height} + ${var.cube_length} + ${aux.offset}",
                  ]
                end_pos:
                  !math [
                    "${simul.body_pos} + 0.5*${var.cube_length}",
                    "0.5*${domain.width}",
                    "${simul.plane_height} + ${var.cube_length} + ${aux.offset}",
                  ]
              long_line3:
                dist: 0.0625
                start_pos:
                  !math [
                    "${simul.body_pos} + 0.5*${var.cube_length} + ${aux.offset}",
                    "0.5*${domain.width}",
                    "${simul.plane_height} + ${var.cube_length}",
                  ]
                end_pos:
                  !math [
                    "${simul.body_pos} + 0.5*${var.cube_length} + ${aux.offset}",
                    "0.5*${domain.width}",
                    "${simul.plane_height}",
                  ]
              tr_line1:
                dist: 0.0625
                start_pos:
                  !math [
                    "${simul.body_pos}",
                    "0.5*${domain.width} - 0.5*${var.cube_length} - ${aux.offset}",
                    "${simul.plane_height}",
                  ]
                end_pos:
                  !math [
                    "${simul.body_pos}",
                    "0.5*${domain.width} - 0.5*${var.cube_length} - ${aux.offset}",
                    "${simul.plane_height} + ${var.cube_length}",
                  ]
              tr_line2:
                dist: 0.0625
                start_pos:
                  !math [
                    "${simul.body_pos}",
                    "0.5*${domain.width} - 0.5*${var.cube_length}",
                    "${simul.plane_height} + ${var.cube_length} + ${aux.offset}",
                  ]
                end_pos:
                  !math [
                    "${simul.body_pos}",
                    "0.5*${domain.width} + 0.5*${var.cube_length}",
                    "${simul.plane_height} + ${var.cube_length} + ${aux.offset}",
                  ]
              tr_line3:
                dist: 0.0625
                start_pos:
                  !math [
                    "${simul.body_pos}",
                    "0.5*${domain.width} + 0.5*${var.cube_length} + ${aux.offset}",
                    "${simul.plane_height} + ${var.cube_length}",
                  ]
                end_pos:
                  !math [
                    "${simul.body_pos}",
                    "0.5*${domain.width} + 0.5*${var.cube_length} + ${aux.offset}",
                    "${simul.plane_height}",
                  ]
        spectrum_analysis:
          macrs: ["rho", "u"]
          points:
            point_pressure_top:
              pos:
                !math [
                  "${simul.body_pos}",
                  "0.5*${domain.width}",
                  "${simul.plane_height} + ${var.cube_length} + ${aux.offset}",
                ]
            point_velocity_top:
              pos:
                !math [
                  "${simul.body_pos}",
                  "0.5*${domain.width}",
                  "${simul.plane_height} + 1.14*${var.cube_length}",
                ]
            point_velocity_wake:
              pos:
                !math [
                  "${simul.body_pos} + 1.5*${var.cube_length}",
                  "0.5*${domain.width} + 0.5*${var.cube_length}",
                  "${simul.plane_height} + ${var.cube_length}",
                ]

    domain:
      domain_size:
        x: !math "${domain.length}"
        y: !math "${domain.width}"
        z: !math "${domain.height}"
      block_size: 8
      bodies:
        cube:
          IBM:
            cfg_use: body_wm
            order: 2
          lnas_path: fixture/lnas/basic/cube_no_floor.lnas
          small_triangles: "add"
          transformation:
            scale:
              !math [
                "0.1*${var.cube_length}",
                "0.1*${var.cube_length}",
                "0.1*${var.cube_length}",
              ]
            translation:
              !math [
                "${simul.body_pos} - 0.5*${var.cube_length}",
                "0.5*${domain.width} - 0.5*${var.cube_length}",
                "${simul.plane_height}",
              ]
        full_plane:
          IBM:
            cfg_use: terrain_wm
          lnas_path: fixture/lnas/wind_tunnel/full_plane.lnas
          small_triangles: "add"
          transformation:
            translation: !math [0, 0, "${simul.plane_height}"]
        obstacles_category_II:
          IBM:
            order: 1
          lnas_path: fixture/lnas/wind_tunnel/category_II/plates_Nx160Ny70_6x2_spacing16x32_offset19y.lnas
          volumes_limits:
            body_transformed:
              - start: !math [4, 20, 0]
                end:
                  !math [
                    "${simul.body_pos} - 0.75*${var.cube_length}",
                    "${domain.width} - 20",
                    "${domain.height}",
                  ]
          small_triangles: "add"
          transformation:
            scale:
              !math [
                "64.0*${simul.scale}",
                "64.0*${simul.scale}",
                "32.0*${simul.scale}",
              ]
            translation: !math [0, 0, "${simul.plane_height}"]
      refinement:
        static:
          lvl1:
            volumes_refine:
              - start: [0.0, 55.0, 0.0]
                end: [272.0, 105.0, 32.0]
                lvl: 1
                is_abs: true
          lvl2:
            volumes_refine:
              - start: [0.0, 67.5, 0.0]
                end: [208.0, 92.5, 20.0]
                lvl: 2
                is_abs: true
          lvl3:
            volumes_refine:
              - start: [0.0, 73.75, 2.0]
                end: [176.0, 86.25, 14.0]
                lvl: 3
                is_abs: true
          lvl4:
            volumes_refine:
              - start: [140.0, 76.875, 4.0]
                end: [160.0, 83.125, 11.0]
                lvl: 4
                is_abs: true
          body_refinement:
            bodies:
              - body_name: cube
                lvl: 5
                normal_offsets: !range [-0.25, 0.751, 0.125]
                transformation:
                  translation: !math [0, 0, "-${aux.ref_delta}"]
              - body_name: cube
                lvl: 5
                normal_offsets: !range [-0.25, 0.751, 0.25]
    models:
      precision:
        default: single

      LBM:
        tau: 0.50002
        vel_set: D3Q27
        coll_oper: RRBGK
      initialization:
        sem_field: true
      engine:
        name: CUDA

      BC:
        periodic_dims: [false, false, false]
        BC_map:
          - pos: E
            BC: RegularizedNeumannOutlet
            rho: 1.0
            wall_normal: E
            order: 2

          - pos: F
            BC: Neumann
            wall_normal: F
            order: 1

          - pos: B
            BC: RegularizedHWBB
            wall_normal: B
            order: 1

          - pos: N
            BC: Neumann
            wall_normal: N
            order: 0

          - pos: S
            BC: Neumann
            wall_normal: S
            order: 0
        SEM:
          eddies:
            lengthscale:
              {
                x: !sub "${simul.sigma_sem}",
                y: !sub "${simul.sigma_sem}",
                z: !sub "${simul.sigma_sem}",
              }
            eddies_vol_density: 300
            domain_limits_yz:
              start: !math ["${simul.sigma_sem}", "-${simul.sigma_sem}"]
              end:
                !math [
                  "${domain.width}-${simul.sigma_sem}",
                  "${domain.height}-${simul.sigma_sem}",
                ]
          profile:
            csv_profile_data: "fixture/SEM/category_vprofile/profile_log_cat2_H150_Uh0.06.csv"
            z_offset: !math "${simul.plane_height}"
            length_mul: !math "${simul.scale}"

      LES:
        model: Smagorinsky
        sgs_cte: 0.17

      IBM:
        forces_accomodate_time: 0
        dirac_delta: "3_points"
        body_cfgs:
          default:
            n_iterations: 3
            forces_factor: 1.0
          terrain_wm:
            n_iterations: 3
            forces_factor: 0.5
            kinetic_energy_correction: false
            wall_model:
              name: EqLog
              dist_ref: 3.125
              dist_shell: 0.125
              start_step: 5000
              params:
                z0: !math "0.05*${simul.scale}"
                TDMA_max_error: 1e-04
                TDMA_max_iters: 10
                TDMA_max_div: 51
          body_wm:
            n_iterations: 3
            forces_factor: 0.5
            wall_model:
              name: EqTBL
              dist_ref: 3.125
              dist_shell: 0.125
              start_step: 10000
              params:
                z0: !math "0.05*${simul.scale}"
                TDMA_max_error: 1e-04
                TDMA_max_iters: 10
                TDMA_min_div: 51
                TDMA_max_div: 51

  - name: cubeFlowMultiLevelLESnoCube
    parent: cubeFlowMultilevelLES
    run_simul: true

    n_steps: !math "${simul.dev_time}+${simul.stats_time}"

    data:
      divergence: { frequency: 1 }
      export_IBM_nodes: !not-inherit {}
      probes: !not-inherit
        historic_series:
          velocity:
            macrs: ["u"]
            interval: { frequency: 1, lvl: 0 }
            points:
              pitot:
                pos:
                  !math [
                    "${simul.body_pos}",
                    "0.5*${domain.width}",
                    "${simul.plane_height} + ${var.cube_length}",
                  ]
            lines:
              velocity_profile:
                dist: 0.0625
                start_pos:
                  !math [
                    "${simul.body_pos}",
                    "0.5*${domain.width}",
                    "${simul.plane_height}",
                  ]
                end_pos:
                  !math [
                    "${simul.body_pos}",
                    "0.5*${domain.width}",
                    "${simul.plane_height} + ${var.cube_length}",
                  ]

    domain:
      bodies: !not-inherit
        full_plane:
          IBM:
            cfg_use: terrain_wm
          lnas_path: fixture/lnas/wind_tunnel/full_plane.lnas
          small_triangles: "add"
          transformation:
            translation: !math [0, 0, "${simul.plane_height}"]
        obstacles_category_II:
          IBM:
            order: 1
          lnas_path: fixture/lnas/wind_tunnel/category_II/plates_Nx160Ny70_6x2_spacing16x32_offset19y.lnas
          volumes_limits:
            body_transformed:
              - start: !math [4, 20, 0]
                end:
                  !math [
                    "${simul.body_pos} - 0.75*${var.cube_length}",
                    "${domain.width} - 20",
                    "${domain.height}",
                  ]
          small_triangles: "add"
          transformation:
            scale:
              !math [
                "64.0*${simul.scale}",
                "64.0*${simul.scale}",
                "32.0*${simul.scale}",
              ]
            translation: !math [0, 0, "${simul.plane_height}"]
      refinement: !not-inherit
        static:
          lvl1:
            volumes_refine:
              - start: [0.0, 55.0, 0.0]
                end: [272.0, 105.0, 32.0]
                lvl: 1
                is_abs: true
          lvl2:
            volumes_refine:
              - start: [0.0, 67.5, 0.0]
                end: [208.0, 92.5, 20.0]
                lvl: 2
                is_abs: true
          lvl3:
            volumes_refine:
              - start: [0.0, 73.75, 2.0]
                end: [176.0, 86.25, 12.0]
                lvl: 3
                is_abs: true
          lvl4:
            volumes_refine:
              - start: [140.0, 76.875, 4.0]
                end: [160.0, 83.125, 8.0]
                lvl: 4
                is_abs: true