Find tracks

Find tracks#

Hide code cell source
from __future__ import annotations

from collections.abc import Callable, Iterable
from inspect import Signature

from boilercv_pipeline.dfs import limit_group_size
from boilercv_pipeline.models.column import Col, LinkedCol, convert, rename
from boilercv_pipeline.models.deps import get_slices
from boilercv_pipeline.models.df import GBC
from boilercv_pipeline.models.params.types import DfOrS_T
from boilercv_pipeline.models.path import get_datetime
from boilercv_pipeline.models.subcool import const
from boilercv_pipeline.palettes import cat10, cool
from boilercv_pipeline.plotting import get_cat_colorbar
from boilercv_pipeline.sets import inspect_video, load_video
from boilercv_pipeline.stages import find_objects, get_thermal_data
from boilercv_pipeline.stages.find_tracks import FindTracks as Params
from boilercv_pipeline.units import U
from dev.docs.nbs import get_mode, init
from devtools import pprint
from matplotlib.axes import Axes
from matplotlib.figure import Figure
from matplotlib.pyplot import subplot_mosaic, subplots
from more_itertools import one, only
from numpy import diff, gradient, linalg, log10, logspace, pi, vectorize
from pandas import DataFrame, Series, melt, merge_ordered, read_hdf
from seaborn import lineplot, scatterplot
from trackpy import link, quiet

from boilercv.correlations import GROUPS
from boilercv.correlations import beta as correlations_beta
from boilercv.correlations import nusselt as correlations_nusselt
from boilercv.correlations.types import Corr
from boilercv.data import FRAME, TIME
from boilercv.dimensionless_params import (
    fourier,
    jakob,
    kinematic_viscosity,
    nusselt,
    prandtl,
    reynolds,
    thermal_diffusivity,
)
from boilercv.images import scale_bool

quiet()

PARAMS = None
"""Notebook stage parameters."""
MODE = get_mode()
"""Notebook execution mode."""
PREVIEW_FRAME_COUNT = 50
"""Number of preview frames."""
Params.hide()
Hide code cell source
if isinstance(PARAMS, str):
    params = Params.model_validate_json(PARAMS)
elif MODE == "docs":
    PREVIEW_FRAME_COUNT = 10
    params = Params(
        context=init(mode=MODE),
        include_patterns=const.nb_include_patterns,
        slicer_patterns=const.nb_slicer_patterns,
    )
else:
    params = Params(context=init(mode=MODE), only_sample=True)
params.set_display_options()
data = params.data
dfs = only(params.dfs)
C = params.cols

thermal = read_hdf(params.deps.thermal)
TC = get_thermal_data.Cols()

slices = get_slices(one(params.filled_slicers))
frames_slice = slices.get(FRAME, slice(None))
objects_path = one(params.objects)
objects = read_hdf(objects_path).set_index(C.frame()).loc[frames_slice, :].reset_index()
frames = objects[C.frame()].unique()
OC = find_objects.Cols()

filled_path = one(params.filled)
with inspect_video(filled_path) as filled:
    step_time = diff(filled[TIME].sel({FRAME: frames})[:2])[0]
    objects = (
        read_hdf(objects_path)
        .set_index(C.frame())
        .loc[frames_slice, :]
        .reset_index()
        .assign(**{
            C.time_elapsed(): lambda df: filled.coords[TIME].sel({
                FRAME: df[C.frame()].values
            })
        })
    )


time = get_datetime(objects_path.stem)
subcooling = thermal.set_index(TC.time())[TC.subcool()][time]
cols_to_link = [C.frame, OC.x_tp, OC.y_tp]

# ! To find (202 - 96)
# %matplotlib widget
# from boilercv_pipeline.sets import get_dataset
# _, ax = subplots()
# ax.imshow(get_dataset("2024-07-18T17-44-35", stage="large_sources")[VIDEO].sel(frame=0))

M_PER_PX = U.convert(3 / 8, "in", "m") / (202 - 96)
# PX_PER_M = 20997.3753
U.define(f"px = {M_PER_PX} m")
U.define(f"frames = {step_time} s")

# Minimum bubble diameters
MIN_BUB_D0 = 10 * M_PER_PX
MIN_BUB_DIAMETER = M_PER_PX

# Physical parameters
LATENT_HEAT_OF_VAPORIZATION = 2.23e6  # J/kg
LIQUID_DENSITY = 960  # kg/m^3
LIQUID_DYNAMIC_VISCOSITY = 2.88e-4  # Pa-s
LIQUID_ISOBARIC_SPECIFIC_HEAT = 4213  # J/kg-K
LIQUID_THERMAL_CONDUCTIVITY = 0.676  # W/m-K
VAPOR_DENSITY = 0.804  # kg/m^3


# Plotting
groups = {C.corr[k](): v for k, v in GROUPS.items() if k in C.corr}
"""Groups for mapping to correlations in data."""
GROUP_DRAW_ORDER = ["Group 2", "Group 4", "Group 3", "Group 1", "Ours"]
"""Order to draw groups."""
GROUP_ORDER = sorted(GROUP_DRAW_ORDER)
"""Order to show groups in legend."""
GROUP_SORTER = vectorize(GROUP_DRAW_ORDER.index)
"""Sorter for groups."""
CORRELATIONS_PALETTE = cat10
"""For plotting one approach."""
TRACKS_PALETTE = cool
"""For plotting the other approach."""
MAX_FOURIER = 0.005
"""Maximum Fourier number to plot."""
MAX_BETA = 1.05
"""Maximum dimensionless bubble diameter to plot."""
MAX_NUSSELT = 1000
"""Maximum Nusselt number to plot."""
TRACKS_ALPHA = 0.1
"""Transparency of the tracks."""
TRACKS_SIZE = 10
"""Size of the tracks."""
MAX_BETA_MAE = 0.2
"""Maximum mean absolute error of beta to plot."""
MAX_NUSSELT_ERR = 12000
"""Maximum mean absolute error of nusselt to plot."""
WIDTH_SCALE = 1.48  # 1.215  # 1.48
"""Width to scale plots by."""
HEIGHT_SCALE = 1.000
"""Width to scale plots by."""


def scale_figure(fig: Figure, width: float = WIDTH_SCALE, height: float = HEIGHT_SCALE):
    """Scale up figure size."""
    fig.set_figwidth(width * fig.get_figwidth())
    fig.set_figheight(height * fig.get_figheight())


def get_delta(df: DataFrame, c: LinkedCol) -> Series[float]:
    """Get position time delta across frames."""
    return df.groupby(C.bub(), **GBC)[[c.source()]].diff().fillna(0) / step_time


# def query_lifetime(df: DataFrame) -> DataFrame:
#     """Filter bubbles by lifetime."""
#     return (
#         df.rename(columns={C.bub_visible(): (temp_name := C.bub_visible.no_unit.name)})
#         .query(f"`{temp_name}` > {MINIMUM_LIFETIME}")
#         .rename(columns={temp_name: C.bub_visible()})
#     )


beta_correlations = correlations_beta.get_correlations()
nusselt_correlations = correlations_nusselt.get_correlations()
constants = {
    "Ja": jakob(
        liquid_density=LIQUID_DENSITY,
        vapor_density=VAPOR_DENSITY,
        liquid_isobaric_specific_heat=LIQUID_ISOBARIC_SPECIFIC_HEAT,
        subcooling=subcooling,
        latent_heat_of_vaporization=LATENT_HEAT_OF_VAPORIZATION,
    ),
    "Pr": prandtl(
        dynamic_viscosity=LIQUID_DYNAMIC_VISCOSITY,
        isobaric_specific_heat=LIQUID_ISOBARIC_SPECIFIC_HEAT,
        thermal_conductivity=LIQUID_THERMAL_CONDUCTIVITY,
    ),
    "alpha": 1.0,
    "pi": pi,
    "liquid_kinematic_viscosity": kinematic_viscosity(
        density=LIQUID_DENSITY, dynamic_viscosity=LIQUID_DYNAMIC_VISCOSITY
    ),
    "liquid_thermal_diffusivity": thermal_diffusivity(
        thermal_conductivity=LIQUID_THERMAL_CONDUCTIVITY,
        density=LIQUID_DENSITY,
        isobaric_specific_heat=LIQUID_ISOBARIC_SPECIFIC_HEAT,
    ),
    "liquid_prandtl": prandtl(
        dynamic_viscosity=LIQUID_DYNAMIC_VISCOSITY,
        isobaric_specific_heat=LIQUID_ISOBARIC_SPECIFIC_HEAT,
        thermal_conductivity=LIQUID_THERMAL_CONDUCTIVITY,
    ),
}


def get_corrs(df: DataFrame, kind: Corr) -> DataFrame:
    """Get correlations."""
    return df.assign(**{
        C.corr[label](): (
            corr.expr(**{
                kwd: value
                for kwd, value in {
                    **constants,
                    "Re_b": df[C.bub_reynolds()],
                    "Re_b0": df[C.bub_reynolds0()],
                    "Fo_0": df[C.bub_fourier()],
                    **({} if kind == "beta" else {"beta": df[C.bub_beta()]}),
                }.items()
                if kwd in Signature.from_callable(corr.expr).parameters
            })
        )
        for label, corr in (
            beta_correlations if kind == "beta" else nusselt_correlations
        ).items()
    })


def get_error(df: DataFrame, kind: Corr, rel: bool = True) -> DataFrame:
    """Get error."""
    corrs = [c() for c in C.corr.values()]
    exp = C.bub_beta() if kind == "beta" else C.bub_nusselt()
    return (
        df.set_index([C.bub_fourier(), C.bub()])[[exp, *corrs]]
        .pipe(
            lambda df: df.assign(**{
                c: abs(df[exp] - df[c]) / (df[c] if rel else 1) for c in corrs
            })
        )
        .reset_index()
    )


def set_group_legend(
    fig: Figure,
    ax: Axes,
    loc="lower center",
    bbox_to_anchor=(0.5, 1.0),
    ncol=6,
    width=WIDTH_SCALE,
    height=HEIGHT_SCALE,
):
    """Set legend for correlation groups."""
    legend = ax.legend(
        [
            {
                lab: h
                for h, lab in zip(*ax.get_legend_handles_labels(), strict=False)
                if "Group" in lab or "Ours" in lab
            }[lab]
            for lab in GROUP_ORDER
        ],
        GROUP_ORDER,
        loc=loc,
        bbox_to_anchor=bbox_to_anchor,
        ncol=ncol,
    )
    for handle in legend.legend_handles:  # pyright: ignore[reportAttributeAccessIssue]
        handle.set_alpha(1.0)
    scale_figure(fig, width=width, height=height)


def preview(
    df: DfOrS_T,
    cols: Iterable[Col] | None = None,
    index: Col | None = None,
    f: Callable[[DfOrS_T], DfOrS_T] | None = None,
    ncol: int = 0,
) -> DfOrS_T:
    """Preview a dataframe in the notebook."""
    # fmt: off
    if df.empty:
        display(df)
        return df
    if isinstance(df, Series):
        def _f(df): return (f(df) if f else df).head(16)
    elif C.bub() in df.columns:
        def _f(df): return (_df := f(df) if f else df).groupby(C.bub(), **GBC)[_df.columns].head(4).head(16)  # pyright: ignore[reportRedeclaration]
    else:
        def _f(df): return (f(df) if f else df).head(4).head(16)
    # fmt: on
    df = params.preview(cols=cols, df=df, index=index, f=_f, ncol=ncol)  # pyright: ignore[reportArgumentType]
    return df


pprint(params)
Hide code cell output
FindTracks(
    context={
        'boilercv_pipeline': BoilercvPipelineContext(
            roots=Roots(
                data=PosixPath('/home/runner/work/boilercv/boilercv/docs/data'),
                docs=PosixPath('/home/runner/work/boilercv/boilercv/docs'),
            ),
            kinds={},
            track_kinds=False,
        ),
    },
    deps=Deps(
        context={
            'boilercv_pipeline': BoilercvPipelineContext(
                roots=Roots(
                    data=PosixPath('/home/runner/work/boilercv/boilercv/docs/data'),
                    docs=PosixPath('/home/runner/work/boilercv/boilercv/docs'),
                ),
                kinds={},
                track_kinds=False,
            ),
        },
        stage=PosixPath('/home/runner/work/boilercv/boilercv/packages/pipeline/boilercv_pipeline/stages/find_tracks'),
        nb=PosixPath('/home/runner/work/boilercv/boilercv/docs/notebooks/find_tracks.ipynb'),
        filled=PosixPath('/home/runner/work/boilercv/boilercv/docs/data/filled'),
        objects=PosixPath('/home/runner/work/boilercv/boilercv/docs/data/e230920/objects'),
        thermal=PosixPath('/home/runner/work/boilercv/boilercv/docs/data/e230920/thermal.h5'),
    ),
    outs=Outs(
        context={
            'boilercv_pipeline': BoilercvPipelineContext(
                roots=Roots(
                    data=PosixPath('/home/runner/work/boilercv/boilercv/docs/data'),
                    docs=PosixPath('/home/runner/work/boilercv/boilercv/docs'),
                ),
                kinds={},
                track_kinds=False,
            ),
        },
        dfs=PosixPath('/home/runner/work/boilercv/boilercv/docs/data/e230920/tracks'),
        plots=PosixPath('/home/runner/work/boilercv/boilercv/docs/data/e230920/tracks_plots'),
    ),
    scale=1.3,
    marker_scale=20.0,
    precision=3,
    display_rows=12,
    data=Data(
        context={
            'boilercv_pipeline': BoilercvPipelineContext(
                roots=Roots(
                    data=PosixPath('/home/runner/work/boilercv/boilercv/docs/data'),
                    docs=PosixPath('/home/runner/work/boilercv/boilercv/docs'),
                ),
                kinds={},
                track_kinds=False,
            ),
        },
        dfs=Dfs(
            src=<DataFrame({
            })>,
            dst=<DataFrame({
            })>,
            tracks=<DataFrame({
            })>,
            bubbles=<DataFrame({
            })>,
            beta=<DataFrame({
            })>,
            nusselt=<DataFrame({
            })>,
            beta_err=<DataFrame({
            })>,
            nusselt_err=<DataFrame({
            })>,
        ),
        plots=Plots(
            context={
                'boilercv_pipeline': BoilercvPipelineContext(
                    roots=Roots(
                        data=PosixPath('/home/runner/work/boilercv/boilercv/docs/data'),
                        docs=PosixPath('/home/runner/work/boilercv/boilercv/docs'),
                    ),
                    kinds={},
                    track_kinds=False,
                ),
            },
            bubbles=<Figure size 640x480 with 0 Axes>,
            multi=<Figure size 640x480 with 0 Axes>,
            beta=<Figure size 640x480 with 0 Axes>,
            beta_err=<Figure size 640x480 with 0 Axes>,
            nusselt_err=<Figure size 640x480 with 0 Axes>,
            mae=<Figure size 640x480 with 0 Axes>,
        ),
    ),
    sample='2024-07-18T17-44-35',
    only_sample=False,
    include_patterns=[
        '^.*2024-07-18T17-44-35.*$',
    ],
    frame_count=0,
    frame_step=1,
    slicer_patterns={
        '.+': {
            'frame': Slicer(
                start=None,
                stop=499,
                step=1,
            ),
        },
    },
    filled=[
        PosixPath('/home/runner/work/boilercv/boilercv/docs/data/filled/2024-07-18T17-44-35.nc'),
    ],
    filled_slicers=[
        {
            'frame': Slicer(
                start=None,
                stop=499,
                step=1,
            ),
        },
    ],
    times=[
        '2024-07-18T17-44-35',
    ],
    dfs=[
        PosixPath('/home/runner/work/boilercv/boilercv/docs/data/e230920/tracks/tracks_2024-07-18T17-44-35.h5'),
    ],
    cols=Cols(
        frame=LinkedCol(
            sym='Frame',
            unit='',
            sub='',
            raw='Frame',
            fmt='.0f',
            source=Col(
                sym='frame',
                unit='',
                sub='',
                raw='frame',
                fmt=None,
            ),
            latex='$\\mathsf{Frame}$',
        ),
        contour=LinkedCol(
            sym='Contour',
            unit='',
            sub='',
            raw='Contour',
            fmt='.0f',
            source=Col(
                sym='contour',
                unit='',
                sub='',
                raw='contour',
                fmt=None,
            ),
            latex='$\\mathsf{Contour}$',
        ),
        time_elapsed=Col(
            sym='t',
            unit='s',
            sub='',
            raw='t (s)',
            fmt=None,
            latex='$\\mathsf{t\\ \\left(s\\right)}$',
        ),
        bub=LinkedCol(
            sym='Bubble',
            unit='',
            sub='',
            raw='Bubble',
            fmt='.0f',
            source=Col(
                sym='particle',
                unit='',
                sub='',
                raw='particle',
                fmt=None,
            ),
            latex='$\\mathsf{Bubble}$',
        ),
        bub_visible_frames=Col(
            sym='t',
            unit='frames',
            sub='b, vis',
            raw='t_b, vis (frames)',
            fmt='.0f',
            latex='$\\mathsf{t_{b, vis}\\ \\left(frames\\right)}$',
        ),
        bub_visible=LinkedCol(
            sym='t',
            unit='s',
            sub='b, vis',
            raw='t_b, vis (s)',
            fmt=None,
            source=Col(
                sym='t',
                unit='frames',
                sub='b, vis',
                raw='t_b, vis (frames)',
                fmt='.0f',
            ),
            latex='$\\mathsf{t_{b, vis}\\ \\left(s\\right)}$',
        ),
        x=LinkedCol(
            sym='x',
            unit='m',
            sub='',
            raw='x (m)',
            fmt=None,
            source=LinkedCol(
                sym='x',
                unit='px',
                sub='',
                raw='x (px)',
                fmt=None,
                source=Col(
                    sym='xpx',
                    unit='',
                    sub='',
                    raw='xpx',
                    fmt=None,
                ),
                latex='$\\mathsf{x\\ \\left(px\\right)}$',
            ),
            latex='$\\mathsf{x\\ \\left(m\\right)}$',
        ),
        y=LinkedCol(
            sym='y',
            unit='m',
            sub='',
            raw='y (m)',
            fmt=None,
            source=LinkedCol(
                sym='y',
                unit='px',
                sub='',
                raw='y (px)',
                fmt=None,
                source=Col(
                    sym='ypx',
                    unit='',
                    sub='',
                    raw='ypx',
                    fmt=None,
                ),
                latex='$\\mathsf{y\\ \\left(px\\right)}$',
            ),
            latex='$\\mathsf{y\\ \\left(m\\right)}$',
        ),
        u=LinkedCol(
            sym='u',
            unit='m/s',
            sub='',
            raw='u (m/s)',
            fmt=None,
            source=LinkedCol(
                sym='x',
                unit='m',
                sub='',
                raw='x (m)',
                fmt=None,
                source=LinkedCol(
                    sym='x',
                    unit='px',
                    sub='',
                    raw='x (px)',
                    fmt=None,
                    source=Col(
                        sym='xpx',
                        unit='',
                        sub='',
                        raw='xpx',
                        fmt=None,
                    ),
                    latex='$\\mathsf{x\\ \\left(px\\right)}$',
                ),
            ),
            latex='$\\mathsf{u\\ \\left(m/s\\right)}$',
        ),
        v=LinkedCol(
            sym='v',
            unit='m/s',
            sub='',
            raw='v (m/s)',
            fmt=None,
            source=LinkedCol(
                sym='y',
                unit='m',
                sub='',
                raw='y (m)',
                fmt=None,
                source=LinkedCol(
                    sym='y',
                    unit='px',
                    sub='',
                    raw='y (px)',
                    fmt=None,
                    source=Col(
                        sym='ypx',
                        unit='',
                        sub='',
                        raw='ypx',
                        fmt=None,
                    ),
                    latex='$\\mathsf{y\\ \\left(px\\right)}$',
                ),
            ),
            latex='$\\mathsf{v\\ \\left(m/s\\right)}$',
        ),
        diameter=LinkedCol(
            sym='d',
            unit='m',
            sub='',
            raw='d (m)',
            fmt=None,
            source=Col(
                sym='d',
                unit='px',
                sub='',
                raw='d (px)',
                fmt=None,
                latex='$\\mathsf{d\\ \\left(px\\right)}$',
            ),
            latex='$\\mathsf{d\\ \\left(m\\right)}$',
        ),
        radius_of_gyration=LinkedCol(
            sym='r',
            unit='m',
            sub='',
            raw='r (m)',
            fmt=None,
            source=Col(
                sym='r',
                unit='px',
                sub='',
                raw='r (px)',
                fmt=None,
                latex='$\\mathsf{r\\ \\left(px\\right)}$',
            ),
            latex='$\\mathsf{r\\ \\left(m\\right)}$',
        ),
        distance=Col(
            sym='z',
            unit='m',
            sub='',
            raw='z (m)',
            fmt=None,
            latex='$\\mathsf{z\\ \\left(m\\right)}$',
        ),
        bub_time=Col(
            sym='t',
            unit='s',
            sub='b',
            raw='t_b (s)',
            fmt=None,
            latex='$\\mathsf{t_{b}\\ \\left(s\\right)}$',
        ),
        bub_lifetime=Col(
            sym='t',
            unit='s',
            sub='b,tot',
            raw='t_b,tot (s)',
            fmt=None,
            latex='$\\mathsf{t_{b,tot}\\ \\left(s\\right)}$',
        ),
        bub_t0=Col(
            sym='t',
            unit='s',
            sub='b0',
            raw='t_b0 (s)',
            fmt=None,
            latex='$\\mathsf{t_{b0}\\ \\left(s\\right)}$',
        ),
        bub_d0=Col(
            sym='d',
            unit='m',
            sub='b0',
            raw='d_b0 (m)',
            fmt=None,
            latex='$\\mathsf{d_{b0}\\ \\left(m\\right)}$',
        ),
        bub_x0=Col(
            sym='x',
            unit='m',
            sub='b0',
            raw='x_b0 (m)',
            fmt=None,
            latex='$\\mathsf{x_{b0}\\ \\left(m\\right)}$',
        ),
        bub_y0=Col(
            sym='y',
            unit='m',
            sub='b0',
            raw='y_b0 (m)',
            fmt=None,
            latex='$\\mathsf{y_{b0}\\ \\left(m\\right)}$',
        ),
        bub_u0=Col(
            sym='u',
            unit='m/s',
            sub='b0',
            raw='u_b0 (m/s)',
            fmt=None,
            latex='$\\mathsf{u_{b0}\\ \\left(m/s\\right)}$',
        ),
        bub_v0=Col(
            sym='v',
            unit='m/s',
            sub='b0',
            raw='v_b0 (m/s)',
            fmt=None,
            latex='$\\mathsf{v_{b0}\\ \\left(m/s\\right)}$',
        ),
        max_diam=Col(
            sym='d',
            unit='m',
            sub='b,max',
            raw='d_b,max (m)',
            fmt=None,
            latex='$\\mathsf{d_{b,max}\\ \\left(m\\right)}$',
        ),
        diam_rate_of_change=Col(
            sym='\\dot{d}',
            unit='m/s',
            sub='b',
            raw='\\dot{d}_b (m/s)',
            fmt=None,
            latex='$\\mathsf{\\dot{d}_{b}\\ \\left(m/s\\right)}$',
        ),
        bub_reynolds=Col(
            sym='Re',
            unit='',
            sub='b',
            raw='Re_b',
            fmt=None,
            latex='$\\mathsf{Re_{b}}$',
        ),
        bub_reynolds0=Col(
            sym='Re',
            unit='',
            sub='b0',
            raw='Re_b0',
            fmt=None,
            latex='$\\mathsf{Re_{b0}}$',
        ),
        bub_fourier=Col(
            sym='Fo',
            unit='',
            sub='b',
            raw='Fo_b',
            fmt=None,
            latex='$\\mathsf{Fo_{b}}$',
        ),
        bub_nusselt=Col(
            sym='Nu',
            unit='',
            sub='c',
            raw='Nu_c',
            fmt=None,
            latex='$\\mathsf{Nu_{c}}$',
        ),
        bub_beta=Col(
            sym='β',
            unit='',
            sub='',
            raw='β',
            fmt=None,
            latex='$\\mathsf{β}$',
        ),
        corr={
            'florschuetz_chao_1965': Col(
                sym='Florschuetz and Chao (1965)',
                unit='',
                sub='',
                raw='Florschuetz and Chao (1965)',
                fmt=None,
                latex='$\\mathsf{Florschuetz\\ and\\ Chao\\ (1965)}$',
            ),
            'florschuetz_chao_fitted_1965': Col(
                sym='Fitted Florschuetz and Chao (1965)',
                unit='',
                sub='',
                raw='Fitted Florschuetz and Chao (1965)',
                fmt=None,
                latex='$\\mathsf{Fitted\\ Florschuetz\\ and\\ Chao\\ (1965)}$',
            ),
            'isenberg_sideman_1970': Col(
                sym='Isenberg and Sideman (1970)',
                unit='',
                sub='',
                raw='Isenberg and Sideman (1970)',
                fmt=None,
                latex='$\\mathsf{Isenberg\\ and\\ Sideman\\ (1970)}$',
            ),
            'akiyama_1973': Col(
                sym='Akiyama (1973)',
                unit='',
                sub='',
                raw='Akiyama (1973)',
                fmt=None,
                latex='$\\mathsf{Akiyama\\ (1973)}$',
            ),
            'chen_mayinger_1992': Col(
                sym='Chen and Mayinger (1992)',
                unit='',
                sub='',
                raw='Chen and Mayinger (1992)',
                fmt=None,
                latex='$\\mathsf{Chen\\ and\\ Mayinger\\ (1992)}$',
            ),
            'zeitoun_et_al_1995': Col(
                sym='Zeitoun et al. (1995)',
                unit='',
                sub='',
                raw='Zeitoun et al. (1995)',
                fmt=None,
                latex='$\\mathsf{Zeitoun\\ et\\ al.\\ (1995)}$',
            ),
            'kalman_mori_2002': Col(
                sym='Kalman and Mori (2002)',
                unit='',
                sub='',
                raw='Kalman and Mori (2002)',
                fmt=None,
                latex='$\\mathsf{Kalman\\ and\\ Mori\\ (2002)}$',
            ),
            'warrier_et_al_2002': Col(
                sym='Warrier et al. (2002)',
                unit='',
                sub='',
                raw='Warrier et al. (2002)',
                fmt=None,
                latex='$\\mathsf{Warrier\\ et\\ al.\\ (2002)}$',
            ),
            'yuan_et_al_2009': Col(
                sym='Yuan et al. (2009)',
                unit='',
                sub='',
                raw='Yuan et al. (2009)',
                fmt=None,
                latex='$\\mathsf{Yuan\\ et\\ al.\\ (2009)}$',
            ),
            'lucic_mayinger_2010': Col(
                sym='Lucic and Mayinger (2010)',
                unit='',
                sub='',
                raw='Lucic and Mayinger (2010)',
                fmt=None,
                latex='$\\mathsf{Lucic\\ and\\ Mayinger\\ (2010)}$',
            ),
            'kim_park_2011': Col(
                sym='Kim and Park (2011)',
                unit='',
                sub='',
                raw='Kim and Park (2011)',
                fmt=None,
                latex='$\\mathsf{Kim\\ and\\ Park\\ (2011)}$',
            ),
            'al_issa_et_al_2014': Col(
                sym='Al Issa et al. (2014)',
                unit='',
                sub='',
                raw='Al Issa et al. (2014)',
                fmt=None,
                latex='$\\mathsf{Al\\ Issa\\ et\\ al.\\ (2014)}$',
            ),
            'tang_et_al_2016': Col(
                sym='Tang et al. (2016)',
                unit='',
                sub='',
                raw='Tang et al. (2016)',
                fmt=None,
                latex='$\\mathsf{Tang\\ et\\ al.\\ (2016)}$',
            ),
        },
        sources=[
            LinkedCol(
                sym='Frame',
                unit='',
                sub='',
                raw='Frame',
                fmt='.0f',
                source=Col(
                    sym='frame',
                    unit='',
                    sub='',
                    raw='frame',
                    fmt=None,
                ),
                latex='$\\mathsf{Frame}$',
            ),
            LinkedCol(
                sym='Contour',
                unit='',
                sub='',
                raw='Contour',
                fmt='.0f',
                source=Col(
                    sym='contour',
                    unit='',
                    sub='',
                    raw='contour',
                    fmt=None,
                ),
                latex='$\\mathsf{Contour}$',
            ),
            LinkedCol(
                sym='Bubble',
                unit='',
                sub='',
                raw='Bubble',
                fmt='.0f',
                source=Col(
                    sym='particle',
                    unit='',
                    sub='',
                    raw='particle',
                    fmt=None,
                ),
                latex='$\\mathsf{Bubble}$',
            ),
        ],
    ),
    objects=[
        PosixPath('/home/runner/work/boilercv/boilercv/docs/data/e230920/objects/objects_2024-07-18T17-44-35.h5'),
    ],
)
Hide code cell source
# Linking
SEARCH_RANGE = 10
"""Pixel range to search for the next bubble."""
MEMORY = 100
"""Frames to remember a bubble."""
length = MEMORY // 2

# Track tuning
Y_SURFACE_THRESHOLD = U.convert(250, "px", "m")
"""Vertical position of bubble centroids considered attached to the surface."""
Y_DEPARTURE_THRESHOLD = U.convert(280, "px", "m")
"""Vertical position of bubble centroids considered to have departed the surface."""
# MINIMUM_LIFETIME = 0.01  # 0.005  # s
# """Minimum bubble lifetime to consider."""


def get_init(ser: Series[float], tail: bool = False) -> float:
    """Get initial value of a series."""
    return ser.tail(length).median() if tail else ser.head(length).median()


data.dfs.tracks = preview(
    ncol=12,
    cols=C.tracks,
    df=link(
        # ? TrackPy expects certain column names
        f=objects.rename(columns={c(): c.source.raw for c in cols_to_link}),  # pyright: ignore[reportCallIssue]
        search_range=SEARCH_RANGE,
        memory=MEMORY,
    )
    .pipe(rename, cols_to_link)  # ? Back to our names
    .pipe(C.bub.rename)
    .assign(**{
        C.bub_visible_frames(): lambda df: (
            df.groupby(C.bub(), **GBC)[C.bub()].transform("count") * frames_slice.step
        )
    })
    .pipe(convert, [C.x, C.y, C.diameter, C.radius_of_gyration], U)
    .sort_values(
        [C.bub_visible_frames(), C.bub(), C.frame()], ascending=[False, True, True]
    )
    .assign(**{
        C.bub(): (lambda df: df.groupby(C.bub(), **GBC).ngroup()),
        C.u(): lambda df: get_delta(df, C.u),
        C.v(): lambda df: get_delta(df, C.v),
        C.distance(): lambda df: linalg.norm(df[[C.x(), C.y()]].abs(), axis=1),
    })
    .pipe(convert, [C.bub_visible], U),
)
print(f"Found {data.dfs.tracks[C.bub()].nunique()} bubbles")
data.dfs.bubbles = preview(
    ncol=12,
    cols=C.bubbles,
    # ? Find rows corresponding to stagnant or invalid bubbles
    # .pipe(lambda df: df[df[C.bub_visible()] > MINIMUM_LIFETIME])
    df=data.dfs.tracks.groupby(C.bub(), **GBC)[data.dfs.tracks.columns]
    # .apply(lambda df: df[(df[C.v()] > -0.1) & (df[C.v()] < 0.1)])
    .apply(
        # ? Don't assign any other columns until invalid rows have been filtered out
        lambda df: df.assign(**{
            "bubble_visible_y": lambda df: df[C.y()].pipe(get_init),
            # ? Initial y position is close to the surface
            "began": lambda df: df["bubble_visible_y"] > Y_SURFACE_THRESHOLD,
            # ? When the bubble gets far enough away from the surface
            "departed": lambda df: df[C.y()] < Y_DEPARTURE_THRESHOLD,
        })
    )
    # ? Filter out invalid rows
    .pipe(lambda df: df[df["began"] & df["departed"]])
    .pipe(limit_group_size, C.bub(), 1)
    # ? Groupby again after filtering out invalid rows
    .groupby(C.bub(), **GBC)[data.dfs.tracks.columns]
    # ? Now columns that depend on the initial row (*.iat[0]) can be assigned
    .apply(
        lambda df: df.assign(**{
            C.bub_time(): lambda df: (
                df[C.time_elapsed()] - df[C.time_elapsed()].iat[0]
            ),
            C.bub_lifetime(): lambda df: (
                df[C.bub_time()].iat[-1] - df[C.bub_time()].iat[0]
            ),
        })
    )
    # .pipe(lambda df: df[df[C.bub_time()] > 0])
    .groupby(C.bub(), **GBC)[[C.bub_time(), C.bub_lifetime(), *data.dfs.tracks.columns]]
    # ? Now columns that depend on the initial row (*.iat[0]) can be assigned
    .apply(
        lambda df: df.assign(**{
            C.bub_t0(): lambda df: df[C.bub_time()].pipe(get_init),
            C.bub_x0(): lambda df: df[C.x()].pipe(get_init),
            C.bub_y0(): lambda df: df[C.y()].pipe(get_init),
            C.bub_d0(): lambda df: df[C.diameter()].pipe(get_init),
            C.bub_u0(): lambda df: df[C.u()].pipe(get_init),
            C.bub_v0(): lambda df: df[C.v()].pipe(get_init),
            C.max_diam(): lambda df: df[C.diameter()].max(),
            C.diam_rate_of_change(): lambda df: gradient(
                df[C.diameter()], df[C.bub_time()]
            ),
        })
    )
    .assign(**{"y": lambda df: df[C.y()] - df[C.bub_y0()]})
    .pipe(
        lambda df: df[
            (df[C.bub_d0()] > MIN_BUB_D0) & (df[C.diameter()] > MIN_BUB_DIAMETER)
        ]
    )[[c() for c in C.bubbles]],
)
print(f"{data.dfs.bubbles[C.bub()].nunique()} bubbles remain")
data.plots.bubbles, ax = subplots()
ax.set_xlabel(C.x())
ax.set_ylabel(C.y())
with load_video(
    filled_path, slices={FRAME: frames[:: (len(frames) // PREVIEW_FRAME_COUNT)]}
) as video:
    composite_video = scale_bool(video).max(FRAME).values
    height, width = composite_video.shape[:2]
    ax.imshow(
        ~composite_video, alpha=0.6, extent=(0, width * M_PER_PX, height * M_PER_PX, 0)
    )
palette, _ = get_cat_colorbar(
    ax, palette=TRACKS_PALETTE, data=data.dfs.bubbles, col=C.bub()
)
scatterplot(
    ax=ax,
    edgecolor="none",
    s=TRACKS_SIZE,
    alpha=TRACKS_ALPHA,
    x=C.x(),
    y=C.y(),
    hue=C.bub(),
    legend=False,
    palette=palette,
    data=data.dfs.bubbles.assign(**{c: data.dfs.tracks[c] for c in [C.x(), C.y()]}),
)
data.plots.multi, axs = subplot_mosaic([[C.y(), C.diameter()]])
scale_figure(data.plots.multi, width=2 * WIDTH_SCALE)
for plot, ax in axs.items():
    if plot in [C.v(), C.diam_rate_of_change()]:  # pyright: ignore[reportUnnecessaryContains]  # TODO: Fix this upstream
        ax.set_yscale("log")
    palette, _ = get_cat_colorbar(ax, C.bub(), TRACKS_PALETTE, data.dfs.bubbles)
    scatterplot(
        ax=ax,
        edgecolor="none",
        s=TRACKS_SIZE,
        alpha=TRACKS_ALPHA,
        x=C.bub_time(),
        y=plot,  # pyright: ignore[reportArgumentType] 1.1.356
        hue=C.bub(),
        legend=False,
        palette=palette,
        data=data.dfs.bubbles.assign(**{  # pyright: ignore[reportCallIssue]
            c: data.dfs.tracks[c] for c in axs if c not in data.dfs.bubbles.columns
        }),
    )

\(\mathsf{Frame}\)

\(\mathsf{Contour}\)

\(\mathsf{t\ \left(s\right)}\)

\(\mathsf{Bubble}\)

\(\mathsf{t_{b, vis}\ \left(frames\right)}\)

\(\mathsf{t_{b, vis}\ \left(s\right)}\)

\(\mathsf{x\ \left(m\right)}\)

\(\mathsf{y\ \left(m\right)}\)

\(\mathsf{u\ \left(m/s\right)}\)

\(\mathsf{v\ \left(m/s\right)}\)

\(\mathsf{d\ \left(m\right)}\)

\(\mathsf{r\ \left(m\right)}\)

0

1

0.00

0

500

0.104

0.0177

0.0271

0.00

0.00

0.00202

0.000505

1

1

0.000208

0

500

0.104

0.0177

0.0271

0.00

0.00

0.00202

0.000505

2

1

0.000416

0

500

0.104

0.0177

0.0271

0.00504

0.0122

0.00201

0.000503

3

1

0.000625

0

500

0.104

0.0177

0.0271

-0.0355

0.0339

0.00201

0.000502

0

4

0.00

1

500

0.104

0.0159

0.0238

0.00

0.00

0.00293

0.000733

1

4

0.000208

1

500

0.104

0.0159

0.0238

0.0418

-0.0414

0.00292

0.000729

2

4

0.000416

1

500

0.104

0.0159

0.0237

-0.0576

-0.0217

0.00290

0.000726

3

4

0.000625

1

500

0.104

0.0159

0.0237

0.0306

-0.0981

0.00288

0.000721

0

18

0.00

2

500

0.104

0.00697

0.0114

0.00

0.00

0.00101

0.000252

1

17

0.000208

2

500

0.104

0.00696

0.0114

-0.0282

-0.0447

0.00100

0.000251

2

16

0.000416

2

500

0.104

0.00696

0.0113

-0.0338

-0.0746

0.000991

0.000248

3

14

0.000625

2

500

0.104

0.00696

0.0113

0.0218

-0.198

0.000978

0.000244

0

2

0.00

3

430

0.0894

0.0104

0.0273

0.00

0.00

0.00311

0.000779

1

2

0.000208

3

430

0.0894

0.0104

0.0272

0.162

-0.0376

0.00312

0.000781

2

2

0.000416

3

430

0.0894

0.0105

0.0272

0.0736

-0.0120

0.00312

0.000780

3

2

0.000625

3

430

0.0894

0.0105

0.0272

0.00154

-0.0453

0.00313

0.000782

Found 35 bubbles

\(\mathsf{Bubble}\)

\(\mathsf{t_{b}\ \left(s\right)}\)

\(\mathsf{t_{b,tot}\ \left(s\right)}\)

\(\mathsf{t_{b0}\ \left(s\right)}\)

\(\mathsf{d_{b0}\ \left(m\right)}\)

\(\mathsf{x_{b0}\ \left(m\right)}\)

\(\mathsf{y_{b0}\ \left(m\right)}\)

\(\mathsf{u_{b0}\ \left(m/s\right)}\)

\(\mathsf{v_{b0}\ \left(m/s\right)}\)

\(\mathsf{d_{b,max}\ \left(m\right)}\)

\(\mathsf{\dot{d}_{b}\ \left(m/s\right)}\)

1

0.00

0.104

0.00510

0.00264

0.0157

0.0231

-0.0350

-0.160

0.00293

-0.0845

1

0.000208

0.104

0.00510

0.00264

0.0157

0.0231

-0.0350

-0.160

0.00293

-0.0699

1

0.000416

0.104

0.00510

0.00264

0.0157

0.0231

-0.0350

-0.160

0.00293

-0.0764

1

0.000625

0.104

0.00510

0.00264

0.0157

0.0231

-0.0350

-0.160

0.00293

-0.0810

4

0.00

0.0650

0.00510

0.00190

0.00846

0.0242

-0.0494

-0.186

0.00212

0.0176

4

0.000208

0.0650

0.00510

0.00190

0.00846

0.0242

-0.0494

-0.186

0.00212

0.0147

4

0.000416

0.0650

0.00510

0.00190

0.00846

0.0242

-0.0494

-0.186

0.00212

0.00293

4

0.000624

0.0650

0.00510

0.00190

0.00846

0.0242

-0.0494

-0.186

0.00212

-0.0146

7

0.00

0.0292

0.00510

0.00110

0.0146

0.0239

-0.000972

-0.289

0.00123

-0.0400

7

0.000209

0.0292

0.00510

0.00110

0.0146

0.0239

-0.000972

-0.289

0.00123

-0.0504

7

0.000417

0.0292

0.00510

0.00110

0.0146

0.0239

-0.000972

-0.289

0.00123

-0.0101

7

0.000625

0.0292

0.00510

0.00110

0.0146

0.0239

-0.000972

-0.289

0.00123

-0.0102

8

0.00

0.0294

0.00510

0.00103

0.0125

0.0235

0.0875

-0.282

0.00122

0.0406

8

0.000208

0.0294

0.00510

0.00103

0.0125

0.0235

0.0875

-0.282

0.00122

-0.0410

8

0.000416

0.0294

0.00510

0.00103

0.0125

0.0235

0.0875

-0.282

0.00122

-0.0924

8

0.000624

0.0294

0.00510

0.00103

0.0125

0.0235

0.0875

-0.282

0.00122

-0.0624

7 bubbles remain
../_images/c9e3f2ecc55ccf5775acbd8c915c8f0d1c4e9735d8c15037e42d2f161c8a9fbc.png ../_images/1fe1683fd04870e7fea2fa24cc5d98810aedfd215d512deab69bea3872e38d46.png
Hide code cell source
data.dfs.dst = preview(
    cols=C.dests,
    df=data.dfs.bubbles.assign(**{
        C.bub_reynolds(): reynolds(
            velocity=abs(data.dfs.tracks[C.v()]),
            characteristic_length=data.dfs.tracks[C.diameter()],
            kinematic_viscosity=constants["liquid_kinematic_viscosity"],
        ),
        C.bub_reynolds0(): lambda df: df.groupby(C.bub(), **GBC)[
            C.bub_reynolds()
        ].transform(get_init),
        C.bub_fourier(): fourier(
            initial_bubble_diameter=data.dfs.bubbles[C.bub_d0()],
            liquid_thermal_diffusivity=constants["liquid_thermal_diffusivity"],
            time=data.dfs.bubbles[C.bub_time()],
        ),
        C.bub_nusselt(): nusselt(  # Nu_c
            heat_transfer_coefficient=-(
                2
                * VAPOR_DENSITY
                * LATENT_HEAT_OF_VAPORIZATION
                / subcooling
                * data.dfs.bubbles[C.diam_rate_of_change()]
            ),
            characteristic_length=data.dfs.tracks[C.diameter()] / 2,
            thermal_conductivity=LIQUID_THERMAL_CONDUCTIVITY,
        ),
        C.bub_beta(): (lambda df: data.dfs.tracks[C.diameter()] / df[C.bub_d0()]),
    }).pipe(lambda df: df[df[C.bub_fourier()] < MAX_FOURIER])[[c() for c in C.dests]],
)
print(f"{data.dfs.dst[C.bub()].nunique()} bubbles remain")
data.dfs.beta = preview(
    cols=C.corr_beta, df=data.dfs.dst.pipe(get_corrs, kind="beta"), ncol=6
)[[c() for c in C.corr_beta]]
data.dfs.nusselt = preview(
    cols=C.corr_nusselt, df=data.dfs.dst.pipe(get_corrs, kind="nusselt"), ncol=6
)[[c() for c in C.corr_nusselt]]
data.dfs.beta_err = preview(
    cols=C.corr_beta, df=data.dfs.beta.pipe(get_error, kind="beta"), ncol=6
)[[c() for c in C.err_beta]]
data.dfs.nusselt_err = preview(
    cols=C.corr_nusselt, df=data.dfs.nusselt.pipe(get_error, kind="nusselt"), ncol=6
)[[c() for c in C.err_nusselt]]

\(\mathsf{Bubble}\)

\(\mathsf{Re_{b}}\)

\(\mathsf{Re_{b0}}\)

\(\mathsf{Fo_{b}}\)

\(\mathsf{Nu_{c}}\)

\(\mathsf{β}\)

1

0.00

1.41e+03

0.00

354.

1.11

1

402.

1.41e+03

4.99e-06

290.

1.10

1

210.

1.41e+03

9.98e-06

317.

1.10

1

943.

1.41e+03

1.50e-05

333.

1.09

4

1.57e+03

1.24e+03

0.00

-52.8

1.11

4

540.

1.24e+03

9.63e-06

-44.1

1.11

4

1.23e+03

1.24e+03

1.93e-05

-8.82

1.11

4

1.06e+03

1.24e+03

2.89e-05

43.9

1.11

7

732.

1.02e+03

0.00

70.4

1.12

7

999.

1.02e+03

2.87e-05

88.2

1.11

7

549.

1.02e+03

5.72e-05

17.5

1.10

7

1.09e+03

1.02e+03

8.57e-05

17.8

1.11

8

0.00

956.

0.00

-70.3

1.17

8

617.

956.

3.25e-05

71.5

1.18

8

775.

956.

6.50e-05

158.

1.16

8

728.

956.

9.76e-05

105.

1.15

7 bubbles remain

\(\mathsf{Bubble}\)

\(\mathsf{Fo_{b}}\)

\(\mathsf{β}\)

\(\mathsf{Florschuetz\ and\ Chao\ (1965)}\)

\(\mathsf{Fitted\ Florschuetz\ and\ Chao\ (1965)}\)

\(\mathsf{Isenberg\ and\ Sideman\ (1970)}\)

1

0.00

1.11

1.00

1.00

1.00

1

4.99e-06

1.10

0.979

0.954

0.999

1

9.98e-06

1.10

0.970

0.938

0.998

1

1.50e-05

1.09

0.963

0.926

0.997

4

0.00

1.11

1.00

1.00

1.00

4

9.63e-06

1.11

0.971

0.939

0.998

4

1.93e-05

1.11

0.958

0.917

0.996

4

2.89e-05

1.11

0.949

0.901

0.994

7

0.00

1.12

1.00

1.00

1.00

7

2.87e-05

1.11

0.949

0.901

0.995

7

5.72e-05

1.10

0.928

0.866

0.989

7

8.57e-05

1.11

0.912

0.839

0.984

8

0.00

1.17

1.00

1.00

1.00

8

3.25e-05

1.18

0.946

0.895

0.994

8

6.50e-05

1.16

0.924

0.858

0.988

8

9.76e-05

1.15

0.906

0.830

0.983

\(\mathsf{Bubble}\)

\(\mathsf{Fo_{b}}\)

\(\mathsf{Nu_{c}}\)

\(\mathsf{Florschuetz\ and\ Chao\ (1965)}\)

\(\mathsf{Fitted\ Florschuetz\ and\ Chao\ (1965)}\)

\(\mathsf{Isenberg\ and\ Sideman\ (1970)}\)

1

0.00

354.

-53.2

-53.2

0.00

1

4.99e-06

290.

-56.3

-56.3

13.8

1

9.98e-06

317.

-58.5

-58.5

9.94

1

1.50e-05

333.

-62.9

-62.9

21.1

4

0.00

-52.8

-55.3

-55.3

27.2

4

9.63e-06

-44.1

-54.4

-54.4

15.9

4

1.93e-05

-8.82

-53.9

-53.9

24.0

4

2.89e-05

43.9

-54.2

-54.2

22.4

7

0.00

70.4

-50.8

-50.8

18.6

7

2.87e-05

88.2

-53.9

-53.9

21.7

7

5.72e-05

17.5

-59.6

-59.6

16.1

7

8.57e-05

17.8

-55.6

-55.6

22.6

8

0.00

-70.3

-35.9

-35.9

0.00

8

3.25e-05

71.5

-34.6

-34.6

17.0

8

6.50e-05

158.

-39.1

-39.1

19.1

8

9.76e-05

105.

-42.0

-42.0

18.5

\(\mathsf{Bubble}\)

\(\mathsf{Fo_{b}}\)

\(\mathsf{β}\)

\(\mathsf{Florschuetz\ and\ Chao\ (1965)}\)

\(\mathsf{Fitted\ Florschuetz\ and\ Chao\ (1965)}\)

\(\mathsf{Isenberg\ and\ Sideman\ (1970)}\)

1

0.00

1.11

0.111

0.111

0.111

1

4.99e-06

1.10

0.129

0.158

0.106

1

9.98e-06

1.10

0.134

0.173

0.103

1

1.50e-05

1.09

0.134

0.180

0.0963

4

0.00

1.11

0.107

0.107

0.107

4

9.63e-06

1.11

0.142

0.181

0.111

4

1.93e-05

1.11

0.158

0.211

0.114

4

2.89e-05

1.11

0.169

0.232

0.116

7

0.00

1.12

0.118

0.118

0.118

7

2.87e-05

1.11

0.169

0.232

0.116

7

5.72e-05

1.10

0.183

0.269

0.110

7

8.57e-05

1.11

0.212

0.318

0.124

8

0.00

1.17

0.175

0.175

0.175

8

3.25e-05

1.18

0.250

0.321

0.190

8

6.50e-05

1.16

0.254

0.350

0.172

8

9.76e-05

1.15

0.264

0.381

0.166

\(\mathsf{Bubble}\)

\(\mathsf{Fo_{b}}\)

\(\mathsf{Nu_{c}}\)

\(\mathsf{Florschuetz\ and\ Chao\ (1965)}\)

\(\mathsf{Fitted\ Florschuetz\ and\ Chao\ (1965)}\)

\(\mathsf{Isenberg\ and\ Sideman\ (1970)}\)

1

0.00

354.

-7.64

-7.64

inf

1

4.99e-06

290.

-6.16

-6.16

20.1

1

9.98e-06

317.

-6.41

-6.41

30.9

1

1.50e-05

333.

-6.30

-6.30

14.8

4

0.00

-52.8

-0.0454

-0.0454

2.95

4

9.63e-06

-44.1

-0.191

-0.191

3.77

4

1.93e-05

-8.82

-0.836

-0.836

1.37

4

2.89e-05

43.9

-1.81

-1.81

0.963

7

0.00

70.4

-2.39

-2.39

2.79

7

2.87e-05

88.2

-2.64

-2.64

3.07

7

5.72e-05

17.5

-1.29

-1.29

0.0880

7

8.57e-05

17.8

-1.32

-1.32

0.215

8

0.00

-70.3

-0.956

-0.956

inf

8

3.25e-05

71.5

-3.07

-3.07

3.20

8

6.50e-05

158.

-5.04

-5.04

7.27

8

9.76e-05

105.

-3.51

-3.51

4.70

Hide code cell source
cols = [c() for c in C.corr.values()]
data.plots.beta, ax = subplots()
palette, _ = get_cat_colorbar(
    ax, palette=TRACKS_PALETTE, data=data.dfs.dst, col=C.bub()
)
ax.set_xlim(0, min(MAX_FOURIER, data.dfs.dst[C.bub_fourier()].max()))
ax.set_ylim(0, MAX_BETA)
scatterplot(
    ax=ax,
    edgecolor="none",
    alpha=TRACKS_ALPHA,
    s=TRACKS_SIZE,
    x=C.bub_fourier(),
    y=C.bub_beta(),
    hue=C.bub(),
    legend=False,
    palette=palette,
    data=data.dfs.dst,
)
beta = get_corrs(
    DataFrame({
        C.bub_fourier(): logspace(
            stop=log10(MAX_FOURIER), start=log10(MAX_FOURIER) - 4, num=int(1e4)
        ),
        C.bub_reynolds0(): data.dfs.dst[C.bub_reynolds0()].median(),
        C.bub_reynolds(): data.dfs.dst[C.bub_reynolds()].median(),
    }),
    "beta",
)
lineplot(
    ax=ax,
    palette=CORRELATIONS_PALETTE,
    hue_order=GROUP_DRAW_ORDER,
    x=C.bub_fourier(),
    y=C.bub_beta(),
    dashes=False,
    style="Correlation",
    hue="Group",
    errorbar=None,
    data=(
        melt(
            beta.set_index(C.bub_fourier())[cols],
            var_name="Correlation",
            value_name=C.bub_beta(),
            value_vars=cols,
            ignore_index=False,
        )
        .assign(**{
            C.bub_fourier(): lambda df: df.index,
            "Group": lambda df: df["Correlation"].map(groups),
        })
        .reset_index(drop=True)
        .sort_values("Group")
    ),
)
set_group_legend(data.plots.beta, ax)
../_images/5558a8585d6ace4ab26a013ad50980c6ada7546c82e9876263bee926e437e3ca.png
Hide code cell source
data.plots.beta_err, ax = subplots()
ax.set_ylabel(C.bub_beta())
ax.set_xscale("log")
ax.set_yscale("log")
scatterplot(
    ax=ax,
    edgecolor="none",
    hue_order=GROUP_DRAW_ORDER,
    s=TRACKS_SIZE,
    alpha=TRACKS_ALPHA,
    palette="tab10",
    x=C.bub_fourier(),
    y=C.bub_beta(),
    hue="Group",
    data=(
        melt(
            data.dfs.beta_err.set_index(C.bub_fourier())[
                [c() for c in C.corr.values()]
            ],
            var_name="Correlation",
            value_name=C.bub_beta(),
            value_vars=[c() for c in C.corr.values()],
            ignore_index=False,
        )
        .assign(**{
            C.bub_fourier(): lambda df: df.index,
            "Group": lambda df: df["Correlation"].map(groups),
        })
        .reset_index(drop=True)
        .sort_values("Group", key=GROUP_SORTER)
    ),
)
set_group_legend(data.plots.beta_err, ax)
../_images/2d14eb2dd07be15c9d8f8130ee8f4860564f05e1ee1fbda77c004c1d622dca77.png
Hide code cell source
data.plots.nusselt_err, ax = subplots()
ax.set_ylabel(C.bub_nusselt())
ax.set_xscale("log")
ax.set_yscale("log")
scatterplot(
    ax=ax,
    edgecolor="none",
    hue_order=GROUP_DRAW_ORDER,
    s=TRACKS_SIZE,
    alpha=TRACKS_ALPHA,
    palette="tab10",
    x=C.bub_fourier(),
    y=C.bub_nusselt(),
    hue="Group",
    data=(
        melt(
            data.dfs.nusselt_err.set_index(C.bub_fourier())[
                [c() for c in C.corr.values()]
            ],
            var_name="Correlation",
            value_name=C.bub_nusselt(),
            value_vars=[c() for c in C.corr.values()],
            ignore_index=False,
        )
        .assign(**{
            C.bub_fourier(): lambda df: df.index,
            "Group": lambda df: df["Correlation"].map(groups),
        })
        .reset_index(drop=True)
        .sort_values("Group", key=GROUP_SORTER)
    ),
)
set_group_legend(data.plots.nusselt_err, ax)
../_images/eea3f49e89874d56f406472e0c3dadbecc66aefcc6e84e4fbb8d1d7fbf0c67dd.png
Hide code cell source
pivoted_beta_err = (
    melt(
        data.dfs.beta.groupby(C.bub(), **GBC)[[c() for c in C.corr.values()]]
        .apply(lambda df: df.sum() / len(df))
        .set_index(C.bub()),
        var_name="Correlation",
        value_name=C.bub_beta(),
        value_vars=[c() for c in C.corr.values()],
        ignore_index=False,
    )
    .assign(**{
        C.bub(): lambda df: df.index,
        "Group": lambda df: df["Correlation"].map(groups),
    })
    .reset_index(drop=True)
    .sort_values("Group")
)
pivoted_nusselt_err = (
    melt(
        data.dfs.nusselt.groupby(C.bub(), **GBC)[[c() for c in C.corr.values()]]
        .apply(lambda df: df.sum() / len(df))
        .set_index(C.bub()),
        var_name="Correlation",
        value_name=C.bub_nusselt(),
        value_vars=[c() for c in C.corr.values()],
        ignore_index=False,
    )
    .assign(**{
        C.bub(): lambda df: df.index,
        "Group": lambda df: df["Correlation"].map(groups),
    })
    .reset_index(drop=True)
    .sort_values("Group")
)
data.plots.mae, ax = subplots()
ax.set_xlim(0, 1)
ax.set_yscale("log")
scatterplot(
    ax=ax,
    edgecolor="none",
    hue_order=GROUP_DRAW_ORDER,
    s=TRACKS_SIZE,
    alpha=TRACKS_ALPHA * 4,
    palette="tab10",
    x=C.bub_beta(),
    y=C.bub_nusselt(),
    hue="Group",
    data=(
        merge_ordered(
            pivoted_beta_err.drop(columns="Group").set_index(C.bub()),
            pivoted_nusselt_err.drop(columns="Group").set_index(C.bub()),
            on=[C.bub(), "Correlation"],
        )
        .assign(**{"Group": lambda df: df["Correlation"].map(groups)})
        .sort_values("Group", key=GROUP_SORTER)
    ),
)
set_group_legend(data.plots.mae, ax)
../_images/dfd930759f04a818a22df018e41c11ca4871e4e66eb16f703a61b7586ce55825.png