|
1 | | -#!/usr/bin/env python3 |
| 1 | +import jax.numpy as jnp |
| 2 | +from jax import Array, lax |
| 3 | +from jax.typing import ArrayLike |
| 4 | + |
| 5 | +from ._base import _Material |
| 6 | + |
| 7 | + |
| 8 | +class Newtonian(_Material): |
| 9 | + """Newtonian fluid material model.""" |
| 10 | + |
| 11 | + _props = ("density", "bulk_modulus", "dynamic_viscosity") |
| 12 | + state_vars = ("pressure",) |
| 13 | + |
| 14 | + def __init__(self, material_properties: dict): |
| 15 | + """Create a Newtonian material. |
| 16 | +
|
| 17 | + Parameters |
| 18 | + ---------- |
| 19 | + material_properties: dict |
| 20 | + Dictionary with material properties. For newtonian |
| 21 | + materials, `density`, `bulk_modulus` and `dynamic_viscosity` |
| 22 | + are required keys. |
| 23 | + """ |
| 24 | + self.validate_props(material_properties) |
| 25 | + compressibility = 1 |
| 26 | + |
| 27 | + if material_properties.get("incompressible", False): |
| 28 | + compressibility = 0 |
| 29 | + |
| 30 | + self.properties = { |
| 31 | + **material_properties, |
| 32 | + "compressibility": compressibility, |
| 33 | + } |
| 34 | + |
| 35 | + def __repr__(self): |
| 36 | + return f"Newtonian(props={self.properties})" |
| 37 | + |
| 38 | + def initialize_state_variables(self, nparticles: int) -> dict: |
| 39 | + """Return initial state variables dictionary. |
| 40 | +
|
| 41 | + Parameters |
| 42 | + ---------- |
| 43 | + nparticles : int |
| 44 | + Number of particles being simulated with this material. |
| 45 | +
|
| 46 | + Returns |
| 47 | + ------- |
| 48 | + dict |
| 49 | + Dictionary of state variables initialized with values |
| 50 | + decided by material type. |
| 51 | + """ |
| 52 | + state_vars_dict = {var: jnp.zeros((nparticles, 1)) for var in self.state_vars} |
| 53 | + return state_vars_dict |
| 54 | + |
| 55 | + def _thermodynamic_pressure(self, volumetric_strain: ArrayLike) -> Array: |
| 56 | + return -self.properties["bulk_modulus"] * volumetric_strain |
| 57 | + |
| 58 | + def compute_stress(self, particles): |
| 59 | + """Compute material stress.""" |
| 60 | + ndim = particles.loc.shape[-1] |
| 61 | + if ndim not in {2, 3}: |
| 62 | + raise ValueError(f"Cannot compute stress for {ndim}-d Newotonian material.") |
| 63 | + volumetric_strain_rate = ( |
| 64 | + particles.strain_rate[:, 0] + particles.strain_rate[:, 1] |
| 65 | + ) |
| 66 | + particles.state_vars["pressure"] = ( |
| 67 | + particles.state_vars["pressure"] |
| 68 | + .at[:] |
| 69 | + .add( |
| 70 | + self.properties["compressibility"] |
| 71 | + * self._thermodynamic_pressure(particles.dvolumetric_strain) |
| 72 | + ) |
| 73 | + ) |
| 74 | + |
| 75 | + volumetric_stress_component = self.properties["compressibility"] * ( |
| 76 | + -particles.state_vars["pressure"] |
| 77 | + - (2 * self.properties["dynamic_viscosity"] * volumetric_strain_rate / 3) |
| 78 | + ) |
| 79 | + |
| 80 | + stress = jnp.zeros_like(particles.stress) |
| 81 | + stress = stress.at[:, 0].set( |
| 82 | + volumetric_stress_component |
| 83 | + + 2 * self.properties["dynamic_viscosity"] * particles.strain_rate[:, 0] |
| 84 | + ) |
| 85 | + stress = stress.at[:, 1].set( |
| 86 | + volumetric_stress_component |
| 87 | + + 2 * self.properties["dynamic_viscosity"] * particles.strain_rate[:, 1] |
| 88 | + ) |
| 89 | + |
| 90 | + extra_component_2 = lax.select( |
| 91 | + ndim == 3, |
| 92 | + 2 * self.properties["dynamic_viscosity"] * particles.strain_rate[:, 2], |
| 93 | + jnp.zeros_like(particles.strain_rate[:, 2]), |
| 94 | + ) |
| 95 | + stress = stress.at[:, 2].set(volumetric_stress_component + extra_component_2) |
| 96 | + |
| 97 | + stress = stress.at[:, 3].set( |
| 98 | + self.properties["dynamic_viscosity"] * particles.strain_rate[:, 3] |
| 99 | + ) |
| 100 | + |
| 101 | + component_4 = lax.select( |
| 102 | + ndim == 3, |
| 103 | + self.properties["dynamic_viscosity"] * particles.strain_rate[:, 4], |
| 104 | + jnp.zeros_like(particles.strain_rate[:, 4]), |
| 105 | + ) |
| 106 | + stress = stress.at[:, 4].set(component_4) |
| 107 | + component_5 = lax.select( |
| 108 | + ndim == 3, |
| 109 | + self.properties["dynamic_viscosity"] * particles.strain_rate[:, 5], |
| 110 | + jnp.zeros_like(particles.strain_rate[:, 5]), |
| 111 | + ) |
| 112 | + stress = stress.at[:, 5].set(component_5) |
| 113 | + |
| 114 | + return stress |
0 commit comments