Skip to content
Snippets Groups Projects
Forked from pycodegen / lbmpy
565 commits behind the upstream repository.
parameterization.py 6.96 KiB
import ipywidgets.widgets as widgets
from IPython.display import display
from ipywidgets.widgets import FloatText, Label, VBox, HBox, Select, BoundedFloatText, Button
from lbmpy.relaxationrates import relaxation_rate_from_lattice_viscosity, lattice_viscosity_from_relaxation_rate


class ScalingWidget:
    def __init__(self):
        self.scalingType = Select(options=[r'diffusive (fixed relaxation rate)',
                                           r'acoustic (fixed dt)',
                                           r'fixed lattice velocity'])
        self.physical_length = FloatText(value=0.1)
        self.cells_per_length = FloatText(value=256.0)
        self.max_physical_velocity = FloatText(value=0.01)
        self.dx = FloatText(value=self._get_dx())
        self.kinematic_viscosity = BoundedFloatText(value=1.0, min=0, max=1e12)
        self.omega = BoundedFloatText(min=0, max=2, value=1.9)
        self.dt = FloatText(value=0)
        self.max_lattice_velocity = FloatText(disabled=True)
        self.re = FloatText(disabled=True)

        self.processingUpdate = False

        def make_label(text):
            label_layout = {'width': '200px'}
            return Label(value=text, layout=label_layout)

        def make_buttons(input_widget, inverse=False):
            button_layout = {'width': '20px'}
            factor = 0.5 if inverse else 2.0
            double_btn = Button(description="", button_style='warning', layout=button_layout)
            half_btn = Button(description="", button_style='success', layout=button_layout)

            def double_value(_):
                input_widget.value *= factor

            def half_value(_):
                input_widget.value /= factor

            widgets.jslink((double_btn, 'disabled'), (input_widget, 'disabled'))
            widgets.jslink((half_btn, 'disabled'), (input_widget, 'disabled'))
            double_btn.on_click(double_value)
            half_btn.on_click(half_value)
            return [half_btn, double_btn]

        self.form = VBox([
            HBox([make_label(r'Scaling'), self.scalingType]),
            HBox([make_label(r"Physical Length $[m]$"), self.physical_length]),
            HBox([make_label(r"Max. velocity $[m/s]$"), self.max_physical_velocity]),
            HBox([make_label(r"Cells per length"), self.cells_per_length] + make_buttons(self.cells_per_length, True)),
            HBox([make_label(r"dx"), self.dx] + make_buttons(self.dx)),
            HBox([make_label(r"Kinematic viscosity $10^{-6}[m^2/s]$"), self.kinematic_viscosity]),
            HBox([make_label(r"Relaxation rate $\omega$"), self.omega]),
            HBox([make_label(r"dt"), self.dt] + make_buttons(self.dt)),
            HBox([make_label(r"Max. lattice velocity"), self.max_lattice_velocity]),
            HBox([make_label(r"Re"), self.re]),
        ])

        # Change listeners
        self.physical_length.observe(self._on_physical_length_change, names='value')
        self.cells_per_length.observe(self._on_cells_per_length_change, names='value')
        self.dx.observe(self._on_dx_change, names='value')
        self.physical_length.observe(self._update_re)
        self.kinematic_viscosity.observe(self._update_re)
        self.max_physical_velocity.observe(self._update_re)

        for obj in [self.scalingType, self.kinematic_viscosity, self.omega, self.dt, self.max_lattice_velocity]:
            obj.observe(self._update_free_parameter, names='value')

        self._update_free_parameter()
        self._update_re()

    def _get_dx(self):
        return self.physical_length.value / self.cells_per_length.value

    def _update_dt_from_relaxation_rate_viscosity_and_dx(self):
        if self.omega.value == 0:
            return 0
        lattice_viscosity = lattice_viscosity_from_relaxation_rate(self.omega.value)
        self.dt.value = lattice_viscosity / (self.kinematic_viscosity.value * 1e-6) * self.dx.value ** 2

    def _update_dt_from_dx_and_lattice_velocity(self):
        if self.max_physical_velocity.value == 0:
            return
        self.dt.value = self.max_lattice_velocity.value / self.max_physical_velocity.value * self.dx.value

    def _update_omega_from_viscosity_and_dt_dx(self):
        if self.dx.value == 0:
            return
        lattice_viscosity = self.kinematic_viscosity.value * 1e-6 * self.dt.value / (self.dx.value ** 2)
        self.omega.value = relaxation_rate_from_lattice_viscosity(lattice_viscosity)

    def _update_free_parameter(self, _=None):
        self.dt.disabled = True
        self.omega.disabled = True
        self.max_lattice_velocity.disabled = True

        if self.scalingType.value == r'diffusive (fixed relaxation rate)':
            self.omega.disabled = False
            self._update_dt_from_relaxation_rate_viscosity_and_dx()
            self._update_lattice_velocity_from_dx_dt_and_physical_velocity()
        elif self.scalingType.value == r'acoustic (fixed dt)':
            self._update_omega_from_viscosity_and_dt_dx()
            self._update_dt_from_dx_and_lattice_velocity()
            self.max_lattice_velocity.disabled = False
        elif self.scalingType.value == r'fixed lattice velocity':
            self._update_omega_from_viscosity_and_dt_dx()
            self.dt.disabled = False
            self._update_lattice_velocity_from_dx_dt_and_physical_velocity()
        else:
            raise ValueError("Unknown Scaling Type")

    def _update_lattice_velocity_from_dx_dt_and_physical_velocity(self):
        if self.dx.value == 0:
            return
        self.max_lattice_velocity.value = self.dt.value / self.dx.value * self.max_physical_velocity.value

    def _update_re(self, _=None):
        if self.kinematic_viscosity.value == 0:
            return
        L = self.physical_length.value
        u = self.max_physical_velocity.value
        nu = self.kinematic_viscosity.value * 1e-6
        self.re.value = round(L * u / nu, 7)

    def _on_dx_change(self, _):
        if self.processingUpdate:
            return
        if self.dx.value == 0:
            return
        self.processingUpdate = True
        self.cells_per_length.value = self.physical_length.value / self.dx.value
        self._update_free_parameter()
        self.processingUpdate = False

    def _on_cells_per_length_change(self, _):
        if self.processingUpdate:
            return
        if self.cells_per_length.value == 0:
            return
        self.processingUpdate = True
        self.dx.value = self.physical_length.value / self.cells_per_length.value
        self._update_free_parameter()
        self.processingUpdate = False

    def _on_physical_length_change(self, _):
        if self.cells_per_length.value == 0:
            return
        self.dx.value = self.physical_length.value / self.cells_per_length.value
        self._update_free_parameter()

    def show(self):
        from IPython.display import HTML
        display(HTML("""
        <style>
            button[disabled], html input[disabled] {
                background-color: #eaeaea !important;
            }
        </style>
        """))
        return self.form