From 904e27703e4e871c8724510229f9c37841464538 Mon Sep 17 00:00:00 2001 From: Tulio Date: Mon, 17 Jul 2023 15:15:32 -0500 Subject: [PATCH 01/45] Split the previous PR in 2 and keep the pseudo-refactor here --- doc/operators/composite_materials.rst | 57 ++-- examples/ablation-workshop-mpi.py | 411 ++++++++++++++++++++++++-- mirgecom/gas_model.py | 7 +- mirgecom/materials/__init__.py | 1 + mirgecom/materials/carbon_fiber.py | 37 ++- mirgecom/materials/initializer.py | 85 ++++++ mirgecom/materials/tacot.py | 338 +-------------------- mirgecom/multiphysics/__init__.py | 1 - mirgecom/multiphysics/phenolics.py | 83 ------ mirgecom/wall_model.py | 197 +++++++++--- 10 files changed, 706 insertions(+), 511 deletions(-) create mode 100644 mirgecom/materials/initializer.py delete mode 100644 mirgecom/multiphysics/phenolics.py diff --git a/doc/operators/composite_materials.rst b/doc/operators/composite_materials.rst index e791ecf59..17e83c7ac 100644 --- a/doc/operators/composite_materials.rst +++ b/doc/operators/composite_materials.rst @@ -1,19 +1,12 @@ Wall Degradation Modeling ========================= -Conserved Quantities -^^^^^^^^^^^^^^^^^^^^ -.. autoclass:: mirgecom.wall_model.PorousFlowDependentVars - -Equations of State -^^^^^^^^^^^^^^^^^^ -.. autoclass:: mirgecom.wall_model.WallEOS -.. autoclass:: mirgecom.wall_model.WallDependentVars +.. automodule:: mirgecom.wall_model Model-specific properties ^^^^^^^^^^^^^^^^^^^^^^^^^ The properties of the materials are defined in specific files. These files - are required by :class:`~mirgecom.wall_model.WallEOS`. + are required by :class:`~mirgecom.wall_model.PorousWallEOS`. Carbon fiber ------------ @@ -77,7 +70,7 @@ Carbon Fiber Oxidation From the conserved variables, it is possible to compute the oxidation progress, denoted by - :attr:`~mirgecom.wall_model.WallDependentVars.tau`. + :attr:`~mirgecom.wall_model.PorousWallDependentVars.tau`. As a consequence, the instantaneous material properties will change due to the mass loss. @@ -85,10 +78,10 @@ Carbon Fiber Oxidation :attr:`~mirgecom.eos.GasDependentVars.temperature` is evaluated using Newton iteration based on both :attr:`~mirgecom.eos.PyrometheusMixture.get_internal_energy` and - :attr:`~mirgecom.wall_model.WallEOS.enthalpy`, + :attr:`~mirgecom.wall_model.PorousWallEOS.enthalpy`, as well as their respective derivatives, namely :attr:`~mirgecom.eos.PyrometheusMixture.heat_capacity_cv` and - :attr:`~mirgecom.wall_model.WallEOS.heat_capacity`. + :attr:`~mirgecom.wall_model.PorousWallEOS.heat_capacity`. Note that :mod:`pyrometheus` is used to handle the species properties. Composite Materials @@ -96,14 +89,13 @@ Composite Materials This section covers the response of composite materials made of phenolic resin and carbon fibers. - The carbon fibers are characterized as a highly porous material, - with void fraction $\approx 90\%$. Phenolic resins are added to rigidize - the fibers by permeating the material and filling partially the gaps between - fibers. As the material is heated up by the flow, the resin pyrolysis, i.e., - it degrades and produces gaseous species. + Phenolic resins are added to rigidize the fibers by permeating the + material and filling partially the gaps between fibers, reducing the porousity + to $\approx 80\%$. As the material is heated up by the flow, the resin + pyrolyses, i.e., it degrades and produces gaseous species. - The temporal evolution of wall density is solved in - order to predict the material degradation. As the + The temporal evolution of wall density is solved in order to predict the + material degradation. As the :class:`~mirgecom.materials.tacot.Pyrolysis` progresses, the mass of each $i$ constituents of the resin, denoted by :attr:`~mirgecom.wall_model.PorousFlowDependentVars.material_densities`, @@ -156,40 +148,27 @@ Composite Materials From the conserved variables, it is possible to compute the decomposition status, denoted by - :attr:`~mirgecom.wall_model.WallDependentVars.tau`. + :attr:`~mirgecom.wall_model.PorousWallDependentVars.tau`. This yields the proportion of virgin (unpyrolyzed material) to char (fully pyrolyzed) and, consequently, the different thermophysicochemical properties of the solid phase. Thus, the instantaneous material properties depend on the current state of the material, as well as the :attr:`~mirgecom.eos.GasDependentVars.temperature`. - It is evaluated using Newton iteration based on both - :attr:`~mirgecom.materials.tacot.GasProperties.enthalpy` (tabulated - data) or - :attr:`~mirgecom.eos.PyrometheusMixture.get_internal_energy` (Pyrometheus) - and - :attr:`~mirgecom.wall_model.WallEOS.enthalpy`, - as well as their respective derivatives, namely - :attr:`~mirgecom.materials.tacot.GasProperties.heat_capacity` and - :attr:`~mirgecom.wall_model.WallEOS.heat_capacity`. + It is evaluated using Newton iteration based on + :attr:`~mirgecom.eos.PyrometheusMixture.get_internal_energy` and + :attr:`~mirgecom.wall_model.PorousWallEOS.enthalpy`, + as well as their respective derivatives + :attr:`~mirgecom.eos.PyrometheusMixture.heat_capacity_cv` and + :attr:`~mirgecom.wall_model.PorousWallEOS.heat_capacity`. In *MIRGE-Com*, the solid properties are obtained by fitting polynomials to tabulated data for easy evaluation of the properties based on the temperature. The complete list of properties can be find, for instance, in :mod:`~mirgecom.materials.tacot`. Different materials can be incorporated as separated files. - - The - :class:`~mirgecom.materials.tacot.GasProperties` are obtained based on - tabulated data, assuming chemical equilibrium, and evaluated with splines - for interpolation of the entries. However, the data is currently obtained - by PICA. .. important :: The current implementation follows the description of [Lachaud_2014]_ for type 2 code. Additional details, extensive formulation and references are provided in https://github.com/illinois-ceesd/phenolics-notes - -Helper Functions ----------------- -.. autofunction:: mirgecom.multiphysics.phenolics.initializer diff --git a/examples/ablation-workshop-mpi.py b/examples/ablation-workshop-mpi.py index 0b758b0bd..f862a8f28 100644 --- a/examples/ablation-workshop-mpi.py +++ b/examples/ablation-workshop-mpi.py @@ -1,6 +1,17 @@ -r"""Demonstrate Ablation Workshop case \#2.1.""" +r"""Demonstrate Ablation Workshop case \#2.1. -__copyright__ = "Copyright (C) 2020 University of Illinois Board of Trustees" +.. autoclass:: GasProperties +.. autoclass:: BprimeTable +.. autoclass:: WallTabulatedEOS + +Helper Functions +---------------- +.. autofunction:: eval_spline +.. autofunction:: eval_spline_derivative + +""" + +__copyright__ = "Copyright (C) 2023 University of Illinois Board of Trustees" __license__ = """ Permission is hereby granted, free of charge, to any person obtaining a copy @@ -25,7 +36,10 @@ import logging import gc import numpy as np +import scipy # type: ignore[import] +from scipy.interpolate import CubicSpline # type: ignore[import] +from meshmode.dof_array import DOFArray from meshmode.discretization.connection import FACE_RESTR_ALL from grudge.trace_pair import ( @@ -60,9 +74,14 @@ logmgr_add_device_memory_usage ) from mirgecom.gas_model import ViscousFluidState -from mirgecom.wall_model import PorousFlowDependentVars +from mirgecom.wall_model import ( + PorousFlowDependentVars, + PorousWallDependentVars, + PorousWallEOS +) from mirgecom.fluid import ConservedVars from mirgecom.transport import GasTransportVars +from mirgecom.gas_model import GasModel from logpyle import IntervalTimer, set_dt @@ -76,6 +95,7 @@ class MyRuntimeError(RuntimeError): """Simple exception to kill the simulation.""" + pass @@ -95,6 +115,371 @@ class _TempDiffTag: pass +def initializer(dim, gas_model, material_densities, temperature, + gas_density=None, pressure=None): + """Initialize state of composite material. + + Parameters + ---------- + gas_model + :class:`mirgecom.gas_model.GasModel` + + material_densities: numpy.ndarray + The initial bulk density of each one of the resin constituents. + It has shape ``(nphase,)`` + + temperature: :class:`~meshmode.dof_array.DOFArray` + The initial temperature of the gas+solid + + gas_density: :class:`~meshmode.dof_array.DOFArray` + Optional argument with the gas density. If not provided, the pressure + will be used to evaluate the density. + + pressure: :class:`~meshmode.dof_array.DOFArray` + Optional argument with the gas pressure. It will be used to evaluate + the gas density. + + Returns + ------- + cv: :class:`mirgecom.fluid.ConservedVars` + The conserved variables of the fluid permeating the porous wall. + """ + if gas_density is None and pressure is None: + raise ValueError("Must specify one of 'gas_density' or 'pressure'") + + if not isinstance(temperature, DOFArray): + raise ValueError("Temperature does not have the proper shape") + + tau = gas_model.wall.decomposition_progress(material_densities) + + # gas constant + gas_const = 8314.46261815324/gas_model.eos.molar_mass(temperature) + + if gas_density is None: + eps_gas = gas_model.wall.void_fraction(tau) + eps_rho_gas = eps_gas*pressure/(gas_const*temperature) + + # internal energy (kinetic energy is neglected) + eps_rho_solid = sum(material_densities) + bulk_energy = ( + eps_rho_gas*gas_model.eos.get_internal_energy(temperature) + + eps_rho_solid*gas_model.wall.enthalpy(temperature, tau) + ) + + momentum = make_obj_array([tau*0.0 for i in range(dim)]) + + return ConservedVars(mass=eps_rho_gas, energy=bulk_energy, momentum=momentum) + + +def eval_spline(x, x_bnds, coeffs) -> DOFArray: + r"""Evaluate spline $a(x-x_i)^3 + b(x-x_i)^2 + c(x-x_i) + d$. + + Parameters + ---------- + x: :class:`~meshmode.dof_array.DOFArray` + The value where $f(x)$ will be evaluated. + x_bnds: :class:`numpy.ndarray` with shape ``(m,)`` + The $m$ nodes $x_i$ for the different segments of the spline. + coeffs: :class:`numpy.ndarray` with shape ``(4,m)`` + The 4 coefficients for each segment $i$ of the spline. + """ + actx = x.array_context + + val = x*0.0 + for i in range(0, len(x_bnds)-1): + val = ( + actx.np.where(actx.np.less(x, x_bnds[i+1]), + actx.np.where(actx.np.greater_equal(x, x_bnds[i]), + coeffs[0, i]*(x-x_bnds[i])**3 + coeffs[1, i]*(x-x_bnds[i])**2 + + coeffs[2, i]*(x-x_bnds[i]) + coeffs[3, i], 0.0), 0.0)) + val + + return val + + +def eval_spline_derivative(x, x_bnds, coeffs) -> DOFArray: + """Evaluate analytical derivative of a spline $3a(x-x_i)^2 + 2b(x-x_i) + c$. + + Parameters + ---------- + x: :class:`~meshmode.dof_array.DOFArray` + The value where $f(x)$ will be evaluatead. + x_bnds: :class:`numpy.ndarray` with shape ``(m,)`` + The $m$ nodes $x_i$ for the different segments of the spline. + coeffs: :class:`numpy.ndarray` with shape ``(4,m)`` + The 4 coefficients for each segment $i$ of the spline. + """ + actx = x.array_context + + val = x*0.0 + for i in range(0, len(x_bnds)-1): + val = ( + actx.np.where(actx.np.less(x, x_bnds[i+1]), + actx.np.where(actx.np.greater_equal(x, x_bnds[i]), + 3.0*coeffs[0, i]*(x-x_bnds[i])**2 + 2.0*coeffs[1, i]*(x-x_bnds[i]) + + coeffs[2, i], 0.0), 0.0)) + val + + return val + + +class BprimeTable: + """Class containing the table for energy balance at the surface. + + This class is only required for uncoupled cases, where only the wall + portion is evaluated. This is NOT used for fully-coupled cases. + """ + + def __init__(self): + + import mirgecom + path = mirgecom.__path__[0] + "/materials/aw_Bprime.dat" + bprime_table = \ + (np.genfromtxt(path, skip_header=1)[:, 2:6]).reshape((25, 151, 4)) + + # bprime contains: B_g, B_c, Temperature T, Wall enthalpy H_W + self._bounds_T = bprime_table[ 0, :-1:6, 2] # noqa E201 + self._bounds_B = bprime_table[::-1, 0, 0] + self._Bc = bprime_table[::-1, :, 1] + self._Hw = bprime_table[::-1, :-1:6, 3] + + # create spline to interpolate the wall enthalpy + self._cs_Hw = np.zeros((25, 4, 24)) + for i in range(0, 25): + self._cs_Hw[i, :, :] = scipy.interpolate.CubicSpline( + self._bounds_T, self._Hw[i, :]).c + + +class GasProperties: + """Simplified model of the pyrolysis gas using tabulated data. + + This section is to be used when species conservation is not employed and + the output gas is assumed to be in chemical equilibrium. + The table was extracted from the suplementar material from the + ablation workshop. Some lines were removed to reduce the number of spline + interpolation segments. + + .. automethod:: molar_mass + .. automethod:: enthalpy + .. automethod:: gamma + .. automethod:: get_internal_energy + .. automethod:: heat_capacity_cp + .. automethod:: heat_capacity_cv + .. automethod:: viscosity + .. automethod:: thermal_conductivity + .. automethod:: pressure + """ + + def __init__(self, prandtl=1.0, lewis=1.0): + """Return gas tabulated data and interpolating functions. + + Parameters + ---------- + prandtl: float + the Prandtl number of the mixture. Defaults to 1. + lewis: float + the Lewis number of the mixture. Defaults to 1. + """ + self._prandtl = prandtl + self._lewis = lewis + + # T , M , Cp , gamma , enthalpy, viscosity + gas_data = np.array([ + [200.00, 21.996, 1.5119, 1.3334, -7246.50, 0.086881], + [350.00, 21.995, 1.7259, 1.2807, -7006.30, 0.144380], + [500.00, 21.948, 2.2411, 1.2133, -6715.20, 0.196150], + [650.00, 21.418, 4.3012, 1.1440, -6265.70, 0.243230], + [700.00, 20.890, 6.3506, 1.1242, -6004.60, 0.258610], + [750.00, 19.990, 9.7476, 1.1131, -5607.70, 0.274430], + [800.00, 18.644, 14.029, 1.1116, -5014.40, 0.290920], + [850.00, 17.004, 17.437, 1.1171, -4218.50, 0.307610], + [900.00, 15.457, 17.009, 1.1283, -3335.30, 0.323490], + [975.00, 14.119, 8.5576, 1.1620, -2352.90, 0.344350], + [1025.0, 13.854, 4.7840, 1.1992, -2034.20, 0.356630], + [1100.0, 13.763, 3.5092, 1.2240, -1741.20, 0.373980], + [1150.0, 13.737, 3.9008, 1.2087, -1560.90, 0.385360], + [1175.0, 13.706, 4.8067, 1.1899, -1453.50, 0.391330], + [1200.0, 13.639, 6.2353, 1.1737, -1315.90, 0.397930], + [1275.0, 13.256, 8.4790, 1.1633, -739.700, 0.421190], + [1400.0, 12.580, 9.0239, 1.1583, 353.3100, 0.458870], + [1525.0, 11.982, 11.516, 1.1377, 1608.400, 0.483230], + [1575.0, 11.732, 12.531, 1.1349, 2214.000, 0.487980], + [1625.0, 11.495, 11.514, 1.1444, 2826.800, 0.491950], + [1700.0, 11.255, 7.3383, 1.1849, 3529.400, 0.502120], + [1775.0, 11.139, 5.3118, 1.2195, 3991.000, 0.516020], + [1925.0, 11.046, 4.2004, 1.2453, 4681.800, 0.545280], + [2000.0, 11.024, 4.0784, 1.2467, 4991.300, 0.559860], + [2150.0, 10.995, 4.1688, 1.2382, 5605.400, 0.588820], + [2300.0, 10.963, 4.5727, 1.2214, 6257.300, 0.617610], + [2450.0, 10.914, 5.3049, 1.2012, 6993.500, 0.646380], + [2600.0, 10.832, 6.4546, 1.1815, 7869.600, 0.675410], + [2750.0, 10.701, 8.1450, 1.1650, 8956.900, 0.705000], + [2900.0, 10.503, 10.524, 1.1528, 10347.00, 0.735570], + [3050.0, 10.221, 13.755, 1.1449, 12157.00, 0.767590], + [3200.0, 9.8394, 17.957, 1.1408, 14523.00, 0.801520], + [3350.0, 9.3574, 22.944, 1.1401, 17584.00, 0.837430], + ]) + + self._cs_molar_mass = CubicSpline(gas_data[:, 0], gas_data[:, 1]) + self._cs_gamma = CubicSpline(gas_data[:, 0], gas_data[:, 3]) + self._cs_enthalpy = CubicSpline(gas_data[:, 0], gas_data[:, 4]*1000.0) + self._cs_viscosity = CubicSpline(gas_data[:, 0], gas_data[:, 5]*1e-4) + + def molar_mass(self, temperature: DOFArray) -> DOFArray: + r"""Return the gas molar mass $M$.""" + coeffs = self._cs_molar_mass.c + bnds = self._cs_molar_mass.x + return eval_spline(temperature, bnds, coeffs) + + def enthalpy(self, temperature: DOFArray) -> DOFArray: + r"""Return the gas enthalpy $h_g$.""" + coeffs = self._cs_enthalpy.c + bnds = self._cs_enthalpy.x + return eval_spline(temperature, bnds, coeffs) + + def get_internal_energy(self, temperature: DOFArray) -> DOFArray: + r"""Evaluate the gas internal energy $e$. + + It is evaluated based on the tabulated enthalpy and molar mass as + + .. math:: + e(T) = h(T) - \frac{R}{M} T + """ + gas_const = 8314.46261815324/self.molar_mass(temperature) + return self.enthalpy(temperature) - gas_const*temperature + + def heat_capacity_cp(self, temperature: DOFArray) -> DOFArray: + r"""Return the gas heat capacity at constant pressure $C_{p_g}$. + + The heat capacity is the derivative of the enthalpy. Thus, to improve + accuracy and avoid issues with Newton iteration, this is computed + exactly as the analytical derivative of the spline for the enthalpy. + """ + coeffs = self._cs_enthalpy.c + bnds = self._cs_enthalpy.x + return eval_spline_derivative(temperature, bnds, coeffs) + + def heat_capacity_cv(self, temperature: DOFArray) -> DOFArray: + r"""Return the gas heat capacity at constant volume $C_{v_g}$.""" + return self.heat_capacity_cp(temperature)/self.gamma(temperature) + + def gamma(self, temperature: DOFArray) -> DOFArray: + r"""Return the heat of capacity ratios $\gamma$.""" + coeffs = self._cs_gamma.c + bnds = self._cs_gamma.x + return eval_spline(temperature, bnds, coeffs) + + def viscosity(self, temperature: DOFArray) -> DOFArray: + r"""Return the gas viscosity $\mu$.""" + coeffs = self._cs_viscosity.c + bnds = self._cs_viscosity.x + return eval_spline(temperature, bnds, coeffs) + + def thermal_conductivity(self, temperature: DOFArray) -> DOFArray: + r"""Return the gas thermal conductivity $\kappa_g$. + + .. math:: + \kappa = \frac{\mu C_p}{Pr} + + with gas viscosity $\mu$, heat capacity at constant pressure $C_p$ + and the Prandtl number $Pr$ (default to 1). + """ + cp = self.heat_capacity_cp(temperature) + mu = self.viscosity(temperature) + return mu*cp/self._prandtl + + def pressure(self, cv: ConservedVars, temperature: DOFArray) -> DOFArray: + r"""Return the gas pressure. + + .. math:: + P = \frac{\epsilon_g \rho_g}{\epsilon_g} \frac{R}{M} T + """ + gas_const = 8314.46261815324/self.molar_mass(temperature) + return cv.mass*gas_const*temperature + + +class WallTabulatedEOS(PorousWallEOS): + """EOS for wall using tabulated data. + + Inherits WallEOS and add an temperature-evaluation function exclusive + for TACOT-tabulated data. + """ + + def get_temperature(self, cv, material_densities, tseed, tau, eos, niter=3): + r"""Evaluate the temperature based on solid+gas properties. + + It uses the assumption of thermal equilibrium between solid and fluid. + Newton iteration is used to get the temperature based on the internal + energy/enthalpy and heat capacity for the bulk (solid+gas) material: + + .. math:: + T^{n+1} = T^n - + \frac + {\epsilon_g \rho_g e_g + \rho_s h_s - \rho e} + {\epsilon_g \rho_g C_{v_g} + \epsilon_s \rho_s C_{p_s}} + + Parameters + ---------- + cv: ConservedVars + + The fluid conserved variables + + material_densities: np.ndarray + + The density of the different wall constituents + + tseed: + + Temperature to use as a seed for Netwon iteration + + tau: meshmode.dof_array.DOFArray + + Progress ratio of the phenolics decomposition + + eos: GasProperties + + The class containing the tabulated data for TACOT + + Returns + ------- + temperature: meshmode.dof_array.DOFArray + + The temperature of the gas+solid + + """ + if isinstance(tseed, DOFArray) is False: + temp = tseed + cv.mass*0.0 + else: + temp = tseed*1.0 + + rho_gas = cv.mass + rho_solid = self.solid_density(material_densities) + rhoe = cv.energy + for _ in range(0, niter): + + eps_rho_e = ( + rho_gas*eos.get_internal_energy(temp) + + rho_solid*self.enthalpy(temp, tau)) + + bulk_cp = ( + rho_gas*eos.heat_capacity_cv(temp) + + rho_solid*self.heat_capacity(temp, tau)) + + temp = temp - (eps_rho_e - rhoe)/bulk_cp + + return temp + + def pressure_diffusivity(self, cv: ConservedVars, wdv: PorousWallDependentVars, + viscosity: DOFArray) -> DOFArray: + r"""Return the pressure diffusivity for Darcy flow. + + .. math:: + d_{P} = \epsilon_g \rho_g \frac{\mathbf{K}}{\mu \epsilon_g} + + where $\mu$ is the gas viscosity, $\epsilon_g$ is the void fraction + and $\mathbf{K}$ is the permeability matrix. + """ + return cv.mass*wdv.permeability/(viscosity*wdv.void_fraction) + + def binary_sum(ary): """Sum the elements of an array, creating a log-depth DAG instead of linear.""" n = len(ary) @@ -106,7 +491,6 @@ def binary_sum(ary): @mpi_entry_point def main(actx_class=None, use_logmgr=True, casename=None, restart_file=None): """Demonstrate the ablation workshop test case 2.1.""" - from mpi4py import MPI comm = MPI.COMM_WORLD rank = comm.Get_rank() @@ -207,20 +591,16 @@ def my_wall_bdry(u_minus): # {{{ Initialize wall model - from mirgecom.materials.tacot import WallTabulatedEOS import mirgecom.materials.tacot as my_composite - my_material = my_composite.SolidProperties() - my_gas = my_composite.GasProperties() + my_gas = GasProperties() + bprime_class = BprimeTable() + my_material = my_composite.SolidProperties(char_mass=220.0, virgin_mass=280.0) pyrolysis = my_composite.Pyrolysis() - - bprime_class = my_composite.BprimeTable() - wall_model = WallTabulatedEOS(wall_material=my_material) # }}} - from mirgecom.gas_model import GasModel gas_model = GasModel(eos=my_gas, wall=wall_model) # ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -251,9 +631,7 @@ def my_wall_bdry(u_minus): current_t = 0.0 istep = 0 first_step = 0 - import mirgecom.multiphysics.phenolics as phenolics - cv = phenolics.initializer( - dcoll=dcoll, gas_model=gas_model, + cv = initializer(dim=dim, gas_model=gas_model, material_densities=material_densities, pressure=pressure, temperature=temperature) @@ -507,7 +885,6 @@ def phenolics_operator(dcoll, fluid_state, boundaries, gas_model, pyrolysis, bprime_class=None, pressure_scaling_factor=1.0, penalty_amount=1.0): """Return the RHS of the composite wall.""" - cv = fluid_state.cv dv = fluid_state.dv tv = fluid_state.tv @@ -552,7 +929,7 @@ def phenolics_operator(dcoll, fluid_state, boundaries, gas_model, pyrolysis, mass=pressure_scaling_factor*pressure_viscous_rhs, momentum=cv.momentum*0.0, energy=energy_viscous_rhs, - species_mass=cv.species_mass) + species_mass=cv.species_mass) # this should be empty in this case # ~~~~~ # inviscid RHS, energy equation only @@ -564,7 +941,7 @@ def phenolics_operator(dcoll, fluid_state, boundaries, gas_model, pyrolysis, mass=zeros, momentum=cv.momentum*0.0, energy=-energy_inviscid_rhs, - species_mass=cv.species_mass) + species_mass=cv.species_mass) # this should be empty in this case # ~~~~~ # decomposition for each component of the resin diff --git a/mirgecom/gas_model.py b/mirgecom/gas_model.py index 32f3e0e74..c1fb9a4fb 100644 --- a/mirgecom/gas_model.py +++ b/mirgecom/gas_model.py @@ -72,7 +72,7 @@ GasTransportVars ) from mirgecom.wall_model import ( - WallEOS, + PorousWallEOS, PorousFlowDependentVars ) from mirgecom.utils import normalize_boundaries @@ -93,14 +93,14 @@ class GasModel: .. attribute:: wall - The class :class:`~mirgecom.wall_model.WallEOS` with the + The class :class:`~mirgecom.wall_model.PorousWallEOS` with the wall model that provide properties for porous media flow. None for pure-fluid flows. """ eos: GasEOS transport: Optional[TransportModel] = None - wall: Optional[WallEOS] = None + wall: Optional[PorousWallEOS] = None @dataclass_array_container @@ -390,6 +390,7 @@ def make_fluid_state(cv, gas_model, else: + # TODO per previous review, think of a way to de-couple wall and fluid. # ~~~ we need to squeeze wall_model in gas_model because this is easily # accessible everywhere in the code diff --git a/mirgecom/materials/__init__.py b/mirgecom/materials/__init__.py index 2bdff3e4a..59f0a1dd7 100644 --- a/mirgecom/materials/__init__.py +++ b/mirgecom/materials/__init__.py @@ -25,6 +25,7 @@ """ __doc__ = """ +.. automodule:: mirgecom.materials.initializer .. automodule:: mirgecom.materials.carbon_fiber .. automodule:: mirgecom.materials.tacot """ diff --git a/mirgecom/materials/carbon_fiber.py b/mirgecom/materials/carbon_fiber.py index f04bc96e8..952a75301 100644 --- a/mirgecom/materials/carbon_fiber.py +++ b/mirgecom/materials/carbon_fiber.py @@ -28,12 +28,29 @@ THE SOFTWARE. """ -from typing import Optional import numpy as np +from typing import Optional +from abc import abstractmethod from meshmode.dof_array import DOFArray +from mirgecom.wall_model import PorousWallDegradationModel class Oxidation: + """Abstract interface for wall oxidation model. + + .. automethod:: get_source_terms + """ + + @abstractmethod + def get_source_terms(self, temperature: DOFArray, + tau: DOFArray, rhoY_o2: DOFArray) -> DOFArray: # noqa N803 + r"""Source terms of fiber oxidation.""" + raise NotImplementedError() + + +# TODO per MTC review, can we generalize the oxidation model? +# should we keep this in the driver? +class Y2_Oxidation_Model(Oxidation): # noqa N801 """Evaluate the source terms for the Y2 model of carbon fiber oxidation. .. automethod:: puma_effective_surface_area @@ -62,7 +79,7 @@ def _get_wall_effective_surface_area_fiber(self, progress) -> DOFArray: """ return self.puma_effective_surface_area(progress) - def get_source_terms(self, temperature, tau, ox_mass) -> DOFArray: + def get_source_terms(self, temperature, tau, rhoY_o2) -> DOFArray: # noqa N803 """Return the effective source terms for the oxidation. Parameters @@ -86,10 +103,10 @@ def get_source_terms(self, temperature, tau, ox_mass) -> DOFArray: / (1.0+0.0002*actx.np.exp(13000.0/temperature))) k = alpha*actx.np.sqrt( (univ_gas_const*temperature)/(2.0*np.pi*mw_o2)) - return (mw_co/mw_o2 + mw_o/mw_o2 - 1)*ox_mass*k*eff_surf_area + return (mw_co/mw_o2 + mw_o/mw_o2 - 1)*rhoY_o2*k*eff_surf_area -class SolidProperties: +class SolidProperties(PorousWallDegradationModel): """Evaluate the properties of the solid state containing only fibers. .. automethod:: void_fraction @@ -103,10 +120,10 @@ class SolidProperties: .. automethod:: tortuosity """ - def __init__(self): - """Solid volumetric density considering all resin constituents.""" - self._char_mass = 0.0 - self._virgin_mass = 160.0 + def __init__(self, char_mass, virgin_mass): + """Bulk density considering the porosity and intrinsic density.""" + self._char_mass = char_mass + self._virgin_mass = virgin_mass def void_fraction(self, tau: DOFArray) -> DOFArray: r"""Return the volumetric fraction $\epsilon$ filled with gas. @@ -166,12 +183,12 @@ def thermal_conductivity(self, temperature: DOFArray, # ~~~~~~~~ other properties def volume_fraction(self, tau: DOFArray) -> DOFArray: - r"""Void fraction $\epsilon$ filled by gas around the fibers.""" + r"""Fraction $\phi$ occupied by the solid.""" # FIXME Should this be a quadratic function? return 0.10*tau def permeability(self, tau: DOFArray) -> DOFArray: - r"""Permeability $K$ of the composite material.""" + r"""Permeability $K$ of the porous material.""" # FIXME find a relation to make it change as a function of "tau" return 6.0e-11 + tau*0.0 diff --git a/mirgecom/materials/initializer.py b/mirgecom/materials/initializer.py new file mode 100644 index 000000000..728f823e8 --- /dev/null +++ b/mirgecom/materials/initializer.py @@ -0,0 +1,85 @@ +""":mod:`~mirgecom.materials.initializer` returns wall materials initialization. + +.. autoclass:: SolidWallInitializer +.. autoclass:: PorousWallInitializer +""" + +__copyright__ = """ +Copyright (C) 2023 University of Illinois Board of Trustees +""" + +__license__ = """ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +from pytools.obj_array import make_obj_array +from mirgecom.fluid import make_conserved +from mirgecom.wall_model import SolidWallConservedVars + + +class SolidWallInitializer: + """Initializer for heat conduction only materials.""" + + def __init__(self, temperature): + self._temp = temperature + + def __call__(self, actx, x_vec, wall_model): + mass = wall_model.density() + energy = mass * wall_model.enthalpy(self._temp) + return SolidWallConservedVars(mass=mass, energy=energy) + + +class PorousWallInitializer: + """Initializer for porous materials.""" + + def __init__(self, pressure, temperature, species, material_densities): + + self._pres = pressure + self._y = species + self._temp = temperature + self._wall_density = material_densities + + def __call__(self, actx, x_vec, gas_model): + + zeros = actx.np.zeros_like(x_vec[0]) + + pressure = self._pres + zeros + temperature = self._temp + zeros + species_mass_frac = self._y + zeros + + tau = gas_model.wall.decomposition_progress(self._wall_density) + + eps_gas = gas_model.wall.void_fraction(tau) + eps_rho_gas = eps_gas*gas_model.eos.get_density(pressure, temperature, + species_mass_frac) + + # internal energy (kinetic energy is neglected) + eps_rho_solid = sum(self._wall_density) + bulk_energy = ( + eps_rho_solid*gas_model.wall.enthalpy(temperature, tau) + + eps_rho_gas*gas_model.eos.get_internal_energy(temperature, + species_mass_frac) + ) + + momentum = make_obj_array([zeros, zeros]) + + species_mass = eps_rho_gas*species_mass_frac + + return make_conserved(dim=2, mass=eps_rho_gas, energy=bulk_energy, + momentum=momentum, species_mass=species_mass) diff --git a/mirgecom/materials/tacot.py b/mirgecom/materials/tacot.py index 75e3c230f..05ca32114 100644 --- a/mirgecom/materials/tacot.py +++ b/mirgecom/materials/tacot.py @@ -6,14 +6,6 @@ .. autoclass:: Pyrolysis .. autoclass:: SolidProperties -.. autoclass:: GasProperties -.. autoclass:: BprimeTable -.. autoclass:: WallTabulatedEOS - -Helper Functions ----------------- -.. autofunction:: eval_spline -.. autofunction:: eval_spline_derivative """ __copyright__ = """ @@ -41,40 +33,12 @@ """ import numpy as np -import scipy # type: ignore[import] -from scipy.interpolate import CubicSpline # type: ignore[import] from meshmode.dof_array import DOFArray from pytools.obj_array import make_obj_array -from mirgecom.fluid import ConservedVars -from mirgecom.wall_model import WallDependentVars, WallEOS - - -class BprimeTable: - """Class containing the table for energy balance at the surface. - - This class is only required for uncoupled cases, where only the wall - portion is evaluated. This is NOT used for fully-coupled cases. - """ - - def __init__(self): - - path = __file__.replace("tacot.py", "aw_Bprime.dat") - bprime_table = \ - (np.genfromtxt(path, skip_header=1)[:, 2:6]).reshape((25, 151, 4)) - - # bprime contains: B_g, B_c, Temperature T, Wall enthalpy H_W - self._bounds_T = bprime_table[ 0, :-1:6, 2] # noqa E201 - self._bounds_B = bprime_table[::-1, 0, 0] - self._Bc = bprime_table[::-1, :, 1] - self._Hw = bprime_table[::-1, :-1:6, 3] - - # create spline to interpolate the wall enthalpy - self._cs_Hw = np.zeros((25, 4, 24)) - for i in range(0, 25): - self._cs_Hw[i, :, :] = scipy.interpolate.CubicSpline( - self._bounds_T, self._Hw[i, :]).c +from mirgecom.wall_model import PorousWallDegradationModel +# TODO generalize? keep in the driver? class Pyrolysis: r"""Evaluate the source terms for the pyrolysis decomposition. @@ -118,12 +82,14 @@ def get_source_terms(self, temperature, chi): """ actx = temperature.array_context + # The density parameters are specific for TACOT. They depend on the + # virgin and char volume fraction. return make_obj_array([ # reaction 1 actx.np.where(actx.np.less(temperature, self._Tcrit[0]), - 0.0, ( - -(30.*((chi[0] - 0.00)/30.)**3)*12000. - * actx.np.exp(-8556.000/temperature))), + 0.0, ( + -(30.*((chi[0] - 0.00)/30.)**3)*12000. + * actx.np.exp(-8556.000/temperature))), actx.np.where(actx.np.less(temperature, self._Tcrit[1]), # reaction 2 0.0, ( @@ -133,191 +99,7 @@ def get_source_terms(self, temperature, chi): temperature*0.0]) -def eval_spline(x, x_bnds, coeffs) -> DOFArray: - r"""Evaluate spline $a(x-x_i)^3 + b(x-x_i)^2 + c(x-x_i) + d$. - - Parameters - ---------- - x: :class:`~meshmode.dof_array.DOFArray` - The value where $f(x)$ will be evaluated. - x_bnds: :class:`numpy.ndarray` with shape ``(m,)`` - The $m$ nodes $x_i$ for the different segments of the spline. - coeffs: :class:`numpy.ndarray` with shape ``(4,m)`` - The 4 coefficients for each segment $i$ of the spline. - """ - actx = x.array_context - - val = x*0.0 - for i in range(0, len(x_bnds)-1): - val = ( - actx.np.where(actx.np.less(x, x_bnds[i+1]), - actx.np.where(actx.np.greater_equal(x, x_bnds[i]), - coeffs[0, i]*(x-x_bnds[i])**3 + coeffs[1, i]*(x-x_bnds[i])**2 - + coeffs[2, i]*(x-x_bnds[i]) + coeffs[3, i], 0.0), 0.0)) + val - - return val - - -def eval_spline_derivative(x, x_bnds, coeffs) -> DOFArray: - """Evaluate analytical derivative of a spline $3a(x-x_i)^2 + 2b(x-x_i) + c$. - - Parameters - ---------- - x: :class:`~meshmode.dof_array.DOFArray` - The value where $f(x)$ will be evaluatead. - x_bnds: :class:`numpy.ndarray` with shape ``(m,)`` - The $m$ nodes $x_i$ for the different segments of the spline. - coeffs: :class:`numpy.ndarray` with shape ``(4,m)`` - The 4 coefficients for each segment $i$ of the spline. - """ - actx = x.array_context - - val = x*0.0 - for i in range(0, len(x_bnds)-1): - val = ( - actx.np.where(actx.np.less(x, x_bnds[i+1]), - actx.np.where(actx.np.greater_equal(x, x_bnds[i]), - 3.0*coeffs[0, i]*(x-x_bnds[i])**2 + 2.0*coeffs[1, i]*(x-x_bnds[i]) - + coeffs[2, i], 0.0), 0.0)) + val - - return val - - -class GasProperties: - """Simplified model of the pyrolysis gas using tabulated data. - - This section is to be used when species conservation is not employed and - the output gas is assumed to be in chemical equilibrium. - The table was extracted from the suplementar material from the - ablation workshop. Some lines were removed to reduce the number of spline - interpolation segments. - - .. automethod:: enthalpy - .. automethod:: heat_capacity - .. automethod:: molar_mass - .. automethod:: dMdT - .. automethod:: viscosity - .. automethod:: thermal_conductivity - .. automethod:: pressure - """ - - def __init__(self, prandtl=1.0, lewis=1.0): - """Return gas tabulated data and interpolating functions. - - Parameters - ---------- - prandtl: float - the Prandtl number of the mixture. Defaults to 1. - lewis: float - the Lewis number of the mixture. Defaults to 1. - """ - self._prandtl = prandtl - self._lewis = lewis - - # T , M , Cp , gamma , enthalpy, viscosity - gas_data = np.array([ - [200.00, 21.996, 1.5119, 1.3334, -7246.50, 0.086881], - [350.00, 21.995, 1.7259, 1.2807, -7006.30, 0.144380], - [500.00, 21.948, 2.2411, 1.2133, -6715.20, 0.196150], - [650.00, 21.418, 4.3012, 1.1440, -6265.70, 0.243230], - [700.00, 20.890, 6.3506, 1.1242, -6004.60, 0.258610], - [750.00, 19.990, 9.7476, 1.1131, -5607.70, 0.274430], - [800.00, 18.644, 14.029, 1.1116, -5014.40, 0.290920], - [850.00, 17.004, 17.437, 1.1171, -4218.50, 0.307610], - [900.00, 15.457, 17.009, 1.1283, -3335.30, 0.323490], - [975.00, 14.119, 8.5576, 1.1620, -2352.90, 0.344350], - [1025.0, 13.854, 4.7840, 1.1992, -2034.20, 0.356630], - [1100.0, 13.763, 3.5092, 1.2240, -1741.20, 0.373980], - [1150.0, 13.737, 3.9008, 1.2087, -1560.90, 0.385360], - [1175.0, 13.706, 4.8067, 1.1899, -1453.50, 0.391330], - [1200.0, 13.639, 6.2353, 1.1737, -1315.90, 0.397930], - [1275.0, 13.256, 8.4790, 1.1633, -739.700, 0.421190], - [1400.0, 12.580, 9.0239, 1.1583, 353.3100, 0.458870], - [1525.0, 11.982, 11.516, 1.1377, 1608.400, 0.483230], - [1575.0, 11.732, 12.531, 1.1349, 2214.000, 0.487980], - [1625.0, 11.495, 11.514, 1.1444, 2826.800, 0.491950], - [1700.0, 11.255, 7.3383, 1.1849, 3529.400, 0.502120], - [1775.0, 11.139, 5.3118, 1.2195, 3991.000, 0.516020], - [1925.0, 11.046, 4.2004, 1.2453, 4681.800, 0.545280], - [2000.0, 11.024, 4.0784, 1.2467, 4991.300, 0.559860], - [2150.0, 10.995, 4.1688, 1.2382, 5605.400, 0.588820], - [2300.0, 10.963, 4.5727, 1.2214, 6257.300, 0.617610], - [2450.0, 10.914, 5.3049, 1.2012, 6993.500, 0.646380], - [2600.0, 10.832, 6.4546, 1.1815, 7869.600, 0.675410], - [2750.0, 10.701, 8.1450, 1.1650, 8956.900, 0.705000], - [2900.0, 10.503, 10.524, 1.1528, 10347.00, 0.735570], - [3050.0, 10.221, 13.755, 1.1449, 12157.00, 0.767590], - [3200.0, 9.8394, 17.957, 1.1408, 14523.00, 0.801520], - [3350.0, 9.3574, 22.944, 1.1401, 17584.00, 0.837430], - ]) - - self._data = gas_data - self._cs_molar_mass = CubicSpline(gas_data[:, 0], gas_data[:, 1]) - self._cs_enthalpy = CubicSpline(gas_data[:, 0], gas_data[:, 4]*1000.0) - self._cs_viscosity = CubicSpline(gas_data[:, 0], gas_data[:, 5]*1e-4) - - def enthalpy(self, temperature: DOFArray) -> DOFArray: - r"""Return the gas enthalpy $h_g$.""" - coeffs = self._cs_enthalpy.c - bnds = self._cs_enthalpy.x - return eval_spline(temperature, bnds, coeffs) - - def heat_capacity(self, temperature: DOFArray) -> DOFArray: - r"""Return the gas heat capacity at constant pressure $C_{p_g}$. - - The heat capacity is the derivative of the enthalpy. Thus, to improve - accuracy and avoid issues with Newton iteration, this is computed - exactly as the analytical derivative of the spline for the enthalpy. - """ - coeffs = self._cs_enthalpy.c - bnds = self._cs_enthalpy.x - return eval_spline_derivative(temperature, bnds, coeffs) - - def molar_mass(self, temperature: DOFArray) -> DOFArray: - r"""Return the gas molar mass $M$.""" - coeffs = self._cs_molar_mass.c - bnds = self._cs_molar_mass.x - return eval_spline(temperature, bnds, coeffs) - - def dMdT(self, temperature: DOFArray) -> DOFArray: # noqa N802 - """Return the partial derivative of molar mass wrt temperature. - - This is necessary to evaluate the temperature using Newton iteration. - """ - coeffs = self._cs_molar_mass.c - bnds = self._cs_molar_mass.x - return eval_spline_derivative(temperature, bnds, coeffs) - - def viscosity(self, temperature: DOFArray) -> DOFArray: - r"""Return the gas viscosity $\mu$.""" - coeffs = self._cs_viscosity.c - bnds = self._cs_viscosity.x - return eval_spline(temperature, bnds, coeffs) - - def thermal_conductivity(self, temperature: DOFArray) -> DOFArray: - r"""Return the gas thermal conductivity $\kappa_g$. - - .. math:: - \kappa = \frac{\mu C_p}{Pr} - - with gas viscosity $\mu$, heat capacity at constant pressure $C_p$ - and the Prandtl number $Pr$ (default to 1). - """ - cp = self.heat_capacity(temperature) - mu = self.viscosity(temperature) - return mu*cp/self._prandtl - - def pressure(self, cv: ConservedVars, temperature: DOFArray) -> DOFArray: - r"""Return the gas pressure. - - .. math:: - P = \frac{\epsilon_g \rho_g}{\epsilon_g} \frac{R}{M} T - """ - gas_const = 8314.46261815324/self.molar_mass(temperature) - return cv.mass*gas_const*temperature - - -class SolidProperties: +class SolidProperties(PorousWallDegradationModel): """Evaluate the properties of the solid state containing resin and fibers. A linear weighting between the virgin and chared states is applied to @@ -335,10 +117,13 @@ class SolidProperties: .. automethod:: tortuosity """ - def __init__(self): - """Solid volumetric density considering all resin constituents.""" - self._char_mass = 220.0 - self._virgin_mass = 280.0 + def __init__(self, char_mass, virgin_mass): + """Bulk density considering the porosity and intrinsic density. + + The fiber and all resin components must be considered. + """ + self._char_mass = char_mass + self._virgin_mass = virgin_mass def void_fraction(self, tau: DOFArray) -> DOFArray: r"""Return the volumetric fraction $\epsilon$ filled with gas. @@ -435,96 +220,3 @@ def tortuosity(self, tau: DOFArray) -> DOFArray: virgin = 1.2 char = 1.1 return virgin*tau + char*(1.0 - tau) - - -class WallTabulatedEOS(WallEOS): - """EOS for wall using tabulated data. - - Inherits WallEOS and add an temperature-evaluation function exclusive - for TACOT-tabulated data. - """ - - def get_temperature(self, cv, material_densities, tseed, tau, eos, niter=3): - r"""Evaluate the temperature based on solid+gas properties. - - It uses the assumption of thermal equilibrium between solid and fluid. - Newton iteration is used to get the temperature based on the internal - energy/enthalpy and heat capacity for the bulk (solid+gas) material: - - .. math:: - T^{n+1} = T^n - - \frac - {\epsilon_g \rho_g(h_g - R_g T^n) + \rho_s h_s - \rho e} - {\epsilon_g \rho_g \left( - C_{p_g} - R_g\left[1 - \frac{\partial M}{\partial T} \right] - \right) - + \epsilon_s \rho_s C_{p_s} - } - - Parameters - ---------- - cv: ConservedVars - - The fluid conserved variables - - material_densities: np.ndarray - - The density of the different wall constituents - - tseed: - - Temperature to use as a seed for Netwon iteration - - tau: meshmode.dof_array.DOFArray - - Progress ratio of the phenolics decomposition - - eos: GasProperties - - The class containing the tabulated data for TACOT - - Returns - ------- - temperature: meshmode.dof_array.DOFArray - - The temperature of the gas+solid - - """ - if isinstance(tseed, DOFArray) is False: - temp = tseed + cv.mass*0.0 - else: - temp = tseed*1.0 - - rho_gas = cv.mass - rho_solid = self.solid_density(material_densities) - rhoe = cv.energy - for _ in range(0, niter): - - # gas constant R/M - molar_mass = eos.molar_mass(temp) - gas_const = 8314.46261815324/molar_mass # noqa N806 - - eps_rho_e = ( - rho_gas*(eos.enthalpy(temp) - gas_const*temp) - + rho_solid*self.enthalpy(temp, tau)) - - bulk_cp = ( - rho_gas*(eos.heat_capacity(temp) - - gas_const*(1.0 - temp/molar_mass*eos.dMdT(temp))) - + rho_solid*self.heat_capacity(temp, tau)) - - temp = temp - (eps_rho_e - rhoe)/bulk_cp - - return temp - - def pressure_diffusivity(self, cv: ConservedVars, wdv: WallDependentVars, - viscosity: DOFArray) -> DOFArray: - r"""Return the pressure diffusivity for Darcy flow. - - .. math:: - d_{P} = \epsilon_g \rho_g \frac{\mathbf{K}}{\mu \epsilon_g} - - where $\mu$ is the gas viscosity, $\epsilon_g$ is the void fraction - and $\mathbf{K}$ is the permeability matrix. - """ - return cv.mass*wdv.permeability/(viscosity*wdv.void_fraction) diff --git a/mirgecom/multiphysics/__init__.py b/mirgecom/multiphysics/__init__.py index eff4a39c6..03792ba6c 100644 --- a/mirgecom/multiphysics/__init__.py +++ b/mirgecom/multiphysics/__init__.py @@ -26,7 +26,6 @@ __doc__ = """ .. automodule:: mirgecom.multiphysics.thermally_coupled_fluid_wall -.. automodule:: mirgecom.multiphysics.phenolics .. autofunction:: make_interface_boundaries >>>>>>> main diff --git a/mirgecom/multiphysics/phenolics.py b/mirgecom/multiphysics/phenolics.py deleted file mode 100644 index 1cecacc17..000000000 --- a/mirgecom/multiphysics/phenolics.py +++ /dev/null @@ -1,83 +0,0 @@ -""":mod:`mirgecom.multiphysics.phenolics` handles phenolics modeling.""" - -__copyright__ = """ -Copyright (C) 2023 University of Illinois Board of Trustees -""" - -__license__ = """ -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -""" - -from meshmode.dof_array import DOFArray -from pytools.obj_array import make_obj_array -from mirgecom.fluid import ConservedVars - - -def initializer(dcoll, gas_model, material_densities, temperature, - gas_density=None, pressure=None): - """Initialize state of composite material. - - Parameters - ---------- - gas_model - :class:`mirgecom.gas_model.GasModel` - - material_densities: numpy.ndarray - The initial bulk density of each one of the resin constituents. - It has shape ``(nphase,)`` - - temperature: :class:`~meshmode.dof_array.DOFArray` - The initial temperature of the gas+solid - - gas_density: :class:`~meshmode.dof_array.DOFArray` - Optional argument with the gas density. If not provided, the pressure - will be used to evaluate the density. - - pressure: :class:`~meshmode.dof_array.DOFArray` - Optional argument with the gas pressure. It will be used to evaluate - the gas density. - - Returns - ------- - cv: :class:`mirgecom.fluid.ConservedVars` - The conserved variables of the fluid permeating the porous wall. - """ - if gas_density is None and pressure is None: - raise ValueError("Must specify one of 'gas_density' or 'pressure'") - - if not isinstance(temperature, DOFArray): - raise ValueError("Temperature does not have the proper shape") - - tau = gas_model.wall.decomposition_progress(material_densities) - - # gas constant - gas_const = 8314.46261815324/gas_model.eos.molar_mass(temperature) - - if gas_density is None: - eps_gas = gas_model.wall.void_fraction(tau) - eps_rho_gas = eps_gas*pressure/(gas_const*temperature) - - # internal energy (kinetic energy is neglected) - eps_rho_solid = sum(material_densities) - bulk_energy = ( - eps_rho_gas*(gas_model.eos.enthalpy(temperature) - gas_const*temperature) - + eps_rho_solid*gas_model.wall.enthalpy(temperature, tau) - ) - - momentum = make_obj_array([tau*0.0 for i in range(dcoll.dim)]) - - return ConservedVars(mass=eps_rho_gas, energy=bulk_energy, momentum=momentum) diff --git a/mirgecom/wall_model.py b/mirgecom/wall_model.py index 425b4e97c..bb7ef0b19 100644 --- a/mirgecom/wall_model.py +++ b/mirgecom/wall_model.py @@ -1,9 +1,13 @@ -""":mod:`mirgecom.multiphysics.wall_model` handles the EOS for wall model. +""":mod:`mirgecom.wall_model` handles the EOS for wall model. +.. autoclass:: SolidWallConservedVars +.. autoclass:: SolidWallDependentVars +.. autoclass:: SolidWallState +.. autoclass:: SolidWallEOS .. autoclass:: PorousFlowDependentVars -.. autoclass:: WallEOS -.. autoclass:: WallDependentVars -.. autoclass:: WallDegradationModel +.. autoclass:: PorousWallDependentVars +.. autoclass:: PorousWallDegradationModel +.. autoclass:: PorousWallEOS """ from dataclasses import dataclass @@ -11,16 +15,111 @@ from typing import Union import numpy as np from meshmode.dof_array import DOFArray -from arraycontext import dataclass_array_container +from arraycontext import ( + dataclass_array_container, + with_container_arithmetic, + get_container_context_recursively +) from mirgecom.fluid import ConservedVars from mirgecom.eos import MixtureDependentVars from mirgecom.transport import GasTransportVars +@with_container_arithmetic(bcast_obj_array=False, + bcast_container_types=(DOFArray, np.ndarray), + matmul=True, + _cls_has_array_context_attr=True, + rel_comparison=True) +@dataclass_array_container +@dataclass(frozen=True) +class SolidWallConservedVars: + """Wall conserved variables for heat conduction only material.""" + + mass: DOFArray + energy: DOFArray + + @property + def array_context(self): + """Return an array context for the :class:`SolidWallConservedVars` object.""" + return get_container_context_recursively(self.mass) + + +@dataclass_array_container +@dataclass(frozen=True) +class SolidWallDependentVars: + """Wall dependent variables for heat conduction only materials.""" + + thermal_conductivity: DOFArray + temperature: DOFArray + + +@dataclass_array_container +@dataclass(frozen=True) +class SolidWallState: + """Wall state for heat conduction only materials.""" + + cv: SolidWallConservedVars + dv: SolidWallDependentVars + + +class SolidWallEOS: + """Model for calculating wall quantities for heat conduction only materials.""" + + def __init__(self, density_func, enthalpy_func, heat_capacity_func, + thermal_conductivity_func): + self._density_func = density_func + self._enthalpy_func = enthalpy_func + self._heat_capacity_func = heat_capacity_func + self._thermal_conductivity_func = thermal_conductivity_func + + def density(self): + """Return the wall density for all components.""" + return self._density_func() + + def heat_capacity(self, temperature=None): + """Return the wall heat_capacity for all components.""" + return self._heat_capacity_func(temperature) + + def enthalpy(self, temperature): + """Return the wall enthalpy for all components.""" + return self._enthalpy_func(temperature) + + def thermal_diffusivity(self, mass, temperature, + thermal_conductivity=None): + """Return the wall thermal diffusivity for all components.""" + if thermal_conductivity is None: + thermal_conductivity = self.thermal_conductivity(temperature) + return thermal_conductivity/(mass * self.heat_capacity(temperature)) + + def thermal_conductivity(self, temperature): + """Return the wall thermal conductivity for all components.""" + return self._thermal_conductivity_func(temperature) + + def eval_temperature(self, wv, tseed=None): + """Evaluate the temperature based on the energy.""" + if tseed is not None: + temp = tseed*1.0 + for _ in range(0, 3): + h = self.enthalpy(temp) + cp = self.heat_capacity(temp) + temp = temp - (h - wv.energy/wv.mass)/cp + return temp + + return wv.energy/(self.density()*self.heat_capacity()) + + def dependent_vars(self, wv, tseed=None): + """Return solid wall dependent variables.""" + temperature = self.eval_temperature(wv, tseed) + kappa = self.thermal_conductivity(temperature) + return SolidWallDependentVars( + thermal_conductivity=kappa, + temperature=temperature) + + @dataclass_array_container @dataclass(frozen=True, eq=False) class PorousFlowDependentVars(MixtureDependentVars): - """Dependent variables for the wall model. + """Dependent variables for the (porous) fluid state. .. attribute:: material_densities """ @@ -30,7 +129,7 @@ class PorousFlowDependentVars(MixtureDependentVars): @dataclass_array_container @dataclass(frozen=True, eq=False) -class WallDependentVars: +class PorousWallDependentVars: """Dependent variables for the wall degradation model. .. attribute:: tau @@ -47,8 +146,19 @@ class WallDependentVars: density: DOFArray -class WallDegradationModel: - """Abstract interface for wall degradation model.""" +class PorousWallDegradationModel: + """Abstract interface for wall degradation model. + + .. automethod:: void_fraction + .. automethod:: decomposition_progress + .. automethod:: enthalpy + .. automethod:: heat_capacity + .. automethod:: thermal_conductivity + .. automethod:: volume_fraction + .. automethod:: permeability + .. automethod:: emissivity + .. automethod:: tortuosity + """ @abstractmethod def void_fraction(self, tau: DOFArray) -> DOFArray: @@ -56,9 +166,13 @@ def void_fraction(self, tau: DOFArray) -> DOFArray: raise NotImplementedError() @abstractmethod - def solid_density(self, - material_densities: Union[DOFArray, np.ndarray]) -> DOFArray: - r"""Return the solid density $\epsilon_s \rho_s$.""" + def decomposition_progress(self, mass: DOFArray) -> DOFArray: + r"""Evaluate the progress ratio $\tau$ of the phenolics decomposition.""" + raise NotImplementedError() + + @abstractmethod + def enthalpy(self, temperature: DOFArray, tau: DOFArray) -> DOFArray: + r"""Evaluate the enthalpy $h_s$ of the solid.""" raise NotImplementedError() @abstractmethod @@ -67,13 +181,19 @@ def heat_capacity(self, temperature: DOFArray, tau: DOFArray) -> DOFArray: raise NotImplementedError() @abstractmethod - def enthalpy(self, temperature: DOFArray, tau: DOFArray) -> DOFArray: - r"""Evaluate the enthalpy $h_s$ of the solid.""" + def thermal_conductivity(self, temperature: DOFArray, + tau: DOFArray) -> DOFArray: + r"""Evaluate the thermal conductivity $\kappa$ of the solid.""" raise NotImplementedError() @abstractmethod - def thermal_conductivity(self, temperature: DOFArray, tau: DOFArray) -> DOFArray: - r"""Evaluate the thermal conductivity $\kappa$ of the solid.""" + def volume_fraction(self, tau: DOFArray) -> DOFArray: + r"""Fraction $\phi$ occupied by the solid.""" + raise NotImplementedError() + + @abstractmethod + def permeability(self, tau: DOFArray) -> DOFArray: + r"""Permeability $K$ of the porous material.""" raise NotImplementedError() @abstractmethod @@ -87,15 +207,15 @@ def tortuosity(self, tau: DOFArray) -> DOFArray: raise NotImplementedError() -class WallEOS: +class PorousWallEOS: """Interface for porous material degradation models. - This class evaluates the variables dependent on the wall decomposition state - and its different parts. For that, the user must supply external functions - in order to consider the different materials employed at wall (for instance, - carbon fibers, graphite, alumina, steel etc). The number and types of the - materials is case-dependent and, hence, must be defined in the respective - simulation driver. + This class evaluates the variables dependent on the wall decomposition + state and its different parts. For that, the user must supply external + functions in order to consider the different materials employed at wall + (for instance, carbon fibers, graphite, alumina, steel etc). The number + and types of the materials is case-dependent and, hence, must be defined + in the respective simulation driver. .. automethod:: __init__ .. automethod:: solid_density @@ -104,9 +224,9 @@ class WallEOS: .. automethod:: get_temperature .. automethod:: enthalpy .. automethod:: heat_capacity + .. automethod:: viscosity .. automethod:: thermal_conductivity .. automethod:: species_diffusivity - .. automethod:: viscosity .. automethod:: dependent_vars """ @@ -165,8 +285,10 @@ def get_temperature(self, cv: ConservedVars, {\epsilon_g \rho_g C_{p_g} + \epsilon_s \rho_s C_{p_s}} """ + actx = cv.array_context + if isinstance(tseed, DOFArray) is False: - temp = tseed + tau*0.0 + temp = tseed + actx.np.zeros_like(tau) else: temp = tseed*1.0 @@ -200,21 +322,23 @@ def heat_capacity(self, temperature: DOFArray, tau: DOFArray) -> DOFArray: """Return the heat capacity of the test material.""" return self._material.heat_capacity(temperature, tau) + # TODO: maybe create a WallTransportVars? def viscosity(self, temperature: DOFArray, tau: DOFArray, gas_tv: GasTransportVars) -> DOFArray: """Viscosity of the gas through the (porous) wall.""" epsilon = self._material.void_fraction(tau) return gas_tv.viscosity/epsilon + # TODO: maybe create a WallTransportVars? def thermal_conductivity(self, cv: ConservedVars, material_densities: Union[DOFArray, np.ndarray], temperature: DOFArray, tau: DOFArray, gas_tv: GasTransportVars): r"""Return the effective thermal conductivity of the gas+solid. - It is a function of temperature and degradation progress. As the fibers - are oxidized, they reduce their cross area and, consequently, their - hability to conduct heat. + It is a function of temperature and degradation progress. As the + fibers are oxidized, they reduce their cross area and, consequently, + their hability to conduct heat. It is evaluated using a mass-weighted average given by @@ -233,6 +357,7 @@ def thermal_conductivity(self, cv: ConservedVars, return y_s*kappa_s + y_g*kappa_g + # TODO: maybe create a WallTransportVars? def species_diffusivity(self, temperature: DOFArray, tau: DOFArray, gas_tv: GasTransportVars): """Mass diffusivity of gaseous species through the (porous) wall. @@ -245,15 +370,17 @@ def species_diffusivity(self, temperature: DOFArray, tau: DOFArray, tortuosity = self._material.tortuosity(tau) return gas_tv.species_diffusivity/tortuosity - def permeability(self, tau: DOFArray) -> DOFArray: - r"""Permeability $K$ of the porous material.""" - return self._material.permeability(tau) + def dependent_vars(self, material_densities: Union[DOFArray, np.ndarray]): + """Get the state-dependent variables. - def dependent_vars(self, - material_densities: Union[DOFArray, np.ndarray]) -> WallDependentVars: - """Get the state-dependent variables.""" + Returns + ------- + dependent_vars: :class:`PorousWallDependentVars` + dependent variables of the wall-only state. These are complementary + to the fluid dependent variables, but not stacked together. + """ tau = self.decomposition_progress(material_densities) - return WallDependentVars( + return PorousWallDependentVars( tau=tau, void_fraction=self.void_fraction(tau), emissivity=self._material.emissivity(tau), From 155f768686555d31576cddbf02453dbd6ecc1732 Mon Sep 17 00:00:00 2001 From: Tulio Date: Mon, 17 Jul 2023 15:45:26 -0500 Subject: [PATCH 02/45] Put mask_from_elements in simutil --- mirgecom/simutil.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/mirgecom/simutil.py b/mirgecom/simutil.py index 62c927a46..8d76333cb 100644 --- a/mirgecom/simutil.py +++ b/mirgecom/simutil.py @@ -22,6 +22,7 @@ Mesh and element utilities -------------------------- +.. autofunction:: mask_from_elements .. autofunction:: geometric_mesh_partitioner .. autofunction:: distribute_mesh .. autofunction:: get_number_of_tetrahedron_nodes @@ -85,6 +86,24 @@ from mpi4py.MPI import Comm +def mask_from_elements(vol_discr, actx, elements): + """Get the individual sub-components from the mesh.""" + mesh = vol_discr.mesh + zeros = vol_discr.zeros(actx) + + group_arrays = [] + + for igrp in range(len(mesh.groups)): + start_elem_nr = mesh.base_element_nrs[igrp] + end_elem_nr = start_elem_nr + mesh.groups[igrp].nelements + grp_elems = elements[ + (elements >= start_elem_nr) + & (elements < end_elem_nr)] - start_elem_nr + grp_ary_np = actx.to_numpy(zeros[igrp]) + grp_ary_np[grp_elems] = 1 + group_arrays.append(actx.from_numpy(grp_ + + def get_number_of_tetrahedron_nodes(dim, order, include_faces=False): """Get number of nodes (modes) in *dim* Tetrahedron of *order*.""" # number of {nodes, modes} see e.g.: From 7b35aa4cd72fd0bd481269fe84090f6397899d3f Mon Sep 17 00:00:00 2001 From: Tulio Date: Mon, 17 Jul 2023 15:47:45 -0500 Subject: [PATCH 03/45] Get the rest of the copy-and-paste in simutil --- mirgecom/simutil.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mirgecom/simutil.py b/mirgecom/simutil.py index 8d76333cb..3865d3344 100644 --- a/mirgecom/simutil.py +++ b/mirgecom/simutil.py @@ -101,7 +101,9 @@ def mask_from_elements(vol_discr, actx, elements): & (elements < end_elem_nr)] - start_elem_nr grp_ary_np = actx.to_numpy(zeros[igrp]) grp_ary_np[grp_elems] = 1 - group_arrays.append(actx.from_numpy(grp_ + group_arrays.append(actx.from_numpy(grp_ary_np)) + + return DOFArray(actx, tuple(group_arrays)) def get_number_of_tetrahedron_nodes(dim, order, include_faces=False): From 7c1f834a0696d281d93b980d91add083c1e10257 Mon Sep 17 00:00:00 2001 From: Tulio Date: Mon, 17 Jul 2023 20:22:56 -0500 Subject: [PATCH 04/45] Fix typo --- mirgecom/wall_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mirgecom/wall_model.py b/mirgecom/wall_model.py index bb7ef0b19..d700aa889 100644 --- a/mirgecom/wall_model.py +++ b/mirgecom/wall_model.py @@ -338,7 +338,7 @@ def thermal_conductivity(self, cv: ConservedVars, It is a function of temperature and degradation progress. As the fibers are oxidized, they reduce their cross area and, consequently, - their hability to conduct heat. + their ability to conduct heat. It is evaluated using a mass-weighted average given by From 2046995126accb333e5eabbc852d57a76fbe9fac Mon Sep 17 00:00:00 2001 From: Tulio Date: Tue, 18 Jul 2023 10:43:40 -0500 Subject: [PATCH 05/45] Use zeros_like --- examples/ablation-workshop-mpi.py | 12 +++++++----- mirgecom/materials/carbon_fiber.py | 9 ++++++--- mirgecom/materials/tacot.py | 16 ++++++++-------- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/examples/ablation-workshop-mpi.py b/examples/ablation-workshop-mpi.py index f862a8f28..95f875125 100644 --- a/examples/ablation-workshop-mpi.py +++ b/examples/ablation-workshop-mpi.py @@ -150,9 +150,10 @@ def initializer(dim, gas_model, material_densities, temperature, if not isinstance(temperature, DOFArray): raise ValueError("Temperature does not have the proper shape") + actx = temperature.array_context + tau = gas_model.wall.decomposition_progress(material_densities) - # gas constant gas_const = 8314.46261815324/gas_model.eos.molar_mass(temperature) if gas_density is None: @@ -166,7 +167,8 @@ def initializer(dim, gas_model, material_densities, temperature, + eps_rho_solid*gas_model.wall.enthalpy(temperature, tau) ) - momentum = make_obj_array([tau*0.0 for i in range(dim)]) + zeros = actx.np.zeros_like(tau) + momentum = make_obj_array([zeros for i in range(dim)]) return ConservedVars(mass=eps_rho_gas, energy=bulk_energy, momentum=momentum) @@ -185,7 +187,7 @@ def eval_spline(x, x_bnds, coeffs) -> DOFArray: """ actx = x.array_context - val = x*0.0 + val = actx.np.zeros_like(x) for i in range(0, len(x_bnds)-1): val = ( actx.np.where(actx.np.less(x, x_bnds[i+1]), @@ -210,7 +212,7 @@ def eval_spline_derivative(x, x_bnds, coeffs) -> DOFArray: """ actx = x.array_context - val = x*0.0 + val = actx.np.zeros_like(x) for i in range(0, len(x_bnds)-1): val = ( actx.np.where(actx.np.less(x, x_bnds[i+1]), @@ -446,7 +448,7 @@ def get_temperature(self, cv, material_densities, tseed, tau, eos, niter=3): """ if isinstance(tseed, DOFArray) is False: - temp = tseed + cv.mass*0.0 + temp = tseed + actx.np.zeros_like(cv.mass) else: temp = tseed*1.0 diff --git a/mirgecom/materials/carbon_fiber.py b/mirgecom/materials/carbon_fiber.py index 952a75301..39fcb3da3 100644 --- a/mirgecom/materials/carbon_fiber.py +++ b/mirgecom/materials/carbon_fiber.py @@ -190,13 +190,16 @@ def volume_fraction(self, tau: DOFArray) -> DOFArray: def permeability(self, tau: DOFArray) -> DOFArray: r"""Permeability $K$ of the porous material.""" # FIXME find a relation to make it change as a function of "tau" - return 6.0e-11 + tau*0.0 + actx = tau.array_context + return 6.0e-11 + actx.np.zeros_like(tau) def emissivity(self, tau: DOFArray) -> DOFArray: """Emissivity for energy radiation.""" - return 0.9 + tau*0.0 + actx = tau.array_context + return 0.9 + actx.np.zeros_like(tau) def tortuosity(self, tau: DOFArray) -> DOFArray: r"""Tortuosity $\eta$ affects the species diffusivity.""" # FIXME find a relation to make it change as a function of "tau" - return 1.1 + tau*0.0 + actx = tau.array_context + return 1.1 + actx.np.zeros_like(tau) diff --git a/mirgecom/materials/tacot.py b/mirgecom/materials/tacot.py index 05ca32114..03f4599d8 100644 --- a/mirgecom/materials/tacot.py +++ b/mirgecom/materials/tacot.py @@ -82,21 +82,21 @@ def get_source_terms(self, temperature, chi): """ actx = temperature.array_context - # The density parameters are specific for TACOT. They depend on the + # The density parameters are hard-coded for TACOT. They depend on the # virgin and char volume fraction. return make_obj_array([ # reaction 1 actx.np.where(actx.np.less(temperature, self._Tcrit[0]), - 0.0, ( - -(30.*((chi[0] - 0.00)/30.)**3)*12000. - * actx.np.exp(-8556.000/temperature))), + 0.0, ( + -(30.*((chi[0] - 0.00)/30.)**3)*12000. + * actx.np.exp(-8556.000/temperature))), actx.np.where(actx.np.less(temperature, self._Tcrit[1]), # reaction 2 - 0.0, ( - -(90.*((chi[1] - 60.0)/90.)**3)*4.48e9 - * actx.np.exp(-20444.44/temperature))), + 0.0, ( + -(90.*((chi[1] - 60.0)/90.)**3)*4.48e9 + * actx.np.exp(-20444.44/temperature))), # fiber oxidation: include in the RHS but dont do anything with it. - temperature*0.0]) + actx.np.zeros_like(temperature)]) class SolidProperties(PorousWallDegradationModel): From 0a182be8fc2ef23bfe2a5c83565d41f88c4add9d Mon Sep 17 00:00:00 2001 From: Tulio Date: Tue, 18 Jul 2023 22:21:12 -0500 Subject: [PATCH 06/45] flake8 --- examples/ablation-workshop-mpi.py | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/ablation-workshop-mpi.py b/examples/ablation-workshop-mpi.py index 95f875125..63cd50f20 100644 --- a/examples/ablation-workshop-mpi.py +++ b/examples/ablation-workshop-mpi.py @@ -447,6 +447,7 @@ def get_temperature(self, cv, material_densities, tseed, tau, eos, niter=3): The temperature of the gas+solid """ + actx = cv.array_context if isinstance(tseed, DOFArray) is False: temp = tseed + actx.np.zeros_like(cv.mass) else: From fee616967912d9b6a238e2184b2914a60a338fd0 Mon Sep 17 00:00:00 2001 From: Tulio Date: Wed, 19 Jul 2023 16:15:03 -0500 Subject: [PATCH 07/45] More changes everywhere --- doc/operators/composite_materials.rst | 10 +- examples/ablation-workshop-mpi.py | 48 ++++---- mirgecom/gas_model.py | 30 +++-- mirgecom/materials/carbon_fiber.py | 21 ++-- mirgecom/materials/tacot.py | 38 +++---- mirgecom/wall_model.py | 156 +++++++++++++++----------- 6 files changed, 158 insertions(+), 145 deletions(-) diff --git a/doc/operators/composite_materials.rst b/doc/operators/composite_materials.rst index 17e83c7ac..f8e06535c 100644 --- a/doc/operators/composite_materials.rst +++ b/doc/operators/composite_materials.rst @@ -6,7 +6,7 @@ Wall Degradation Modeling Model-specific properties ^^^^^^^^^^^^^^^^^^^^^^^^^ The properties of the materials are defined in specific files. These files - are required by :class:`~mirgecom.wall_model.PorousWallEOS`. + are required by :class:`~mirgecom.wall_model.PorousFlowEOS`. Carbon fiber ------------ @@ -78,10 +78,10 @@ Carbon Fiber Oxidation :attr:`~mirgecom.eos.GasDependentVars.temperature` is evaluated using Newton iteration based on both :attr:`~mirgecom.eos.PyrometheusMixture.get_internal_energy` and - :attr:`~mirgecom.wall_model.PorousWallEOS.enthalpy`, + :attr:`~mirgecom.wall_model.PorousWallProperties.enthalpy`, as well as their respective derivatives, namely :attr:`~mirgecom.eos.PyrometheusMixture.heat_capacity_cv` and - :attr:`~mirgecom.wall_model.PorousWallEOS.heat_capacity`. + :attr:`~mirgecom.wall_model.PorousWallProperties.heat_capacity`. Note that :mod:`pyrometheus` is used to handle the species properties. Composite Materials @@ -156,10 +156,10 @@ Composite Materials :attr:`~mirgecom.eos.GasDependentVars.temperature`. It is evaluated using Newton iteration based on :attr:`~mirgecom.eos.PyrometheusMixture.get_internal_energy` and - :attr:`~mirgecom.wall_model.PorousWallEOS.enthalpy`, + :attr:`~mirgecom.wall_model.PorousWallProperties.enthalpy`, as well as their respective derivatives :attr:`~mirgecom.eos.PyrometheusMixture.heat_capacity_cv` and - :attr:`~mirgecom.wall_model.PorousWallEOS.heat_capacity`. + :attr:`~mirgecom.wall_model.PorousWallProperties.heat_capacity`. In *MIRGE-Com*, the solid properties are obtained by fitting polynomials to tabulated data for easy evaluation of the properties based on the diff --git a/examples/ablation-workshop-mpi.py b/examples/ablation-workshop-mpi.py index 63cd50f20..18f0e045a 100644 --- a/examples/ablation-workshop-mpi.py +++ b/examples/ablation-workshop-mpi.py @@ -77,7 +77,7 @@ from mirgecom.wall_model import ( PorousFlowDependentVars, PorousWallDependentVars, - PorousWallEOS + PorousFlowEOS ) from mirgecom.fluid import ConservedVars from mirgecom.transport import GasTransportVars @@ -398,14 +398,14 @@ def pressure(self, cv: ConservedVars, temperature: DOFArray) -> DOFArray: return cv.mass*gas_const*temperature -class WallTabulatedEOS(PorousWallEOS): +class WallTabulatedEOS(PorousFlowEOS): """EOS for wall using tabulated data. Inherits WallEOS and add an temperature-evaluation function exclusive for TACOT-tabulated data. """ - def get_temperature(self, cv, material_densities, tseed, tau, eos, niter=3): + def get_temperature(self, cv, wdv, tseed, gas_model, niter=3): r"""Evaluate the temperature based on solid+gas properties. It uses the assumption of thermal equilibrium between solid and fluid. @@ -424,19 +424,15 @@ def get_temperature(self, cv, material_densities, tseed, tau, eos, niter=3): The fluid conserved variables - material_densities: np.ndarray + wdv: PorousWallDependentVars - The density of the different wall constituents + Wall properties as a function of decomposition progress tseed: Temperature to use as a seed for Netwon iteration - tau: meshmode.dof_array.DOFArray - - Progress ratio of the phenolics decomposition - - eos: GasProperties + gas_model: GasModel The class containing the tabulated data for TACOT @@ -453,20 +449,17 @@ def get_temperature(self, cv, material_densities, tseed, tau, eos, niter=3): else: temp = tseed*1.0 - rho_gas = cv.mass - rho_solid = self.solid_density(material_densities) - rhoe = cv.energy for _ in range(0, niter): eps_rho_e = ( - rho_gas*eos.get_internal_energy(temp) - + rho_solid*self.enthalpy(temp, tau)) + cv.mass*gas_model.eos.get_internal_energy(temp) + + wdv.density*self._material.enthalpy(temp, wdv.tau)) bulk_cp = ( - rho_gas*eos.heat_capacity_cv(temp) - + rho_solid*self.heat_capacity(temp, tau)) + cv.mass*gas_model.eos.heat_capacity_cv(temp) + + wdv.density*self._material.heat_capacity(temp, wdv.tau)) - temp = temp - (eps_rho_e - rhoe)/bulk_cp + temp = temp - (eps_rho_e - cv.energy)/bulk_cp return temp @@ -635,7 +628,7 @@ def my_wall_bdry(u_minus): istep = 0 first_step = 0 cv = initializer(dim=dim, gas_model=gas_model, - material_densities=material_densities, + material_densities=material_densities, material=my_material, pressure=pressure, temperature=temperature) # stand-alone version of the "gas_model" to bypass some unnecessary @@ -650,14 +643,13 @@ def make_state(cv, temperature_seed, material_densities): """ zeros = actx.np.zeros_like(cv.mass) - tau = gas_model.wall.decomposition_progress(material_densities) - epsilon = gas_model.wall.void_fraction(tau) + wdv = gas_model.wall.dependent_vars(material_densities) + temperature = gas_model.wall.get_temperature( - cv=cv, material_densities=material_densities, - tseed=temperature_seed, tau=tau, eos=my_gas) + cv=cv, wdv=wdv, tseed=temperature_seed, gas_model=gas_model) - pressure = 1.0/epsilon*gas_model.eos.pressure(cv=cv, - temperature=temperature) + pressure = gas_model.wall.get_pressure( + cv=cv, wdv=wdv, temperature=temperature, gas_model=gas_model) dv = PorousFlowDependentVars( temperature=temperature, @@ -678,11 +670,10 @@ def make_state(cv, temperature_seed, material_densities): species_diffusivity=cv.species_mass) # coupled solid-gas thermal conductivity - kappa = wall_model.thermal_conductivity( - cv, material_densities, temperature, tau, gas_tv) + kappa = wall_model.thermal_conductivity(cv, wdv, temperature, gas_tv) # coupled solid-gas viscosity - viscosity = wall_model.viscosity(temperature, tau, gas_tv) + viscosity = wall_model.viscosity(wdv, temperature, gas_tv) # return modified transport vars tv = GasTransportVars( @@ -1075,7 +1066,6 @@ def my_post_step(step, t, dt, state): if step == first_step + 1: with gc_timer: - import gc gc.collect() # Freeze the objects that are still alive so they will not # be considered in future gc collections. diff --git a/mirgecom/gas_model.py b/mirgecom/gas_model.py index c1fb9a4fb..0e1ef6b96 100644 --- a/mirgecom/gas_model.py +++ b/mirgecom/gas_model.py @@ -72,7 +72,7 @@ GasTransportVars ) from mirgecom.wall_model import ( - PorousWallEOS, + PorousFlowEOS, PorousFlowDependentVars ) from mirgecom.utils import normalize_boundaries @@ -93,14 +93,13 @@ class GasModel: .. attribute:: wall - The class :class:`~mirgecom.wall_model.PorousWallEOS` with the - wall model that provide properties for porous media flow. + A wall model to provide properties of the solid for porous media flow. None for pure-fluid flows. """ eos: GasEOS transport: Optional[TransportModel] = None - wall: Optional[PorousWallEOS] = None + wall: Optional[PorousFlowEOS] = None @dataclass_array_container @@ -390,22 +389,20 @@ def make_fluid_state(cv, gas_model, else: - # TODO per previous review, think of a way to de-couple wall and fluid. + # FIXME per previous review, think of a way to de-couple wall and fluid. # ~~~ we need to squeeze wall_model in gas_model because this is easily # accessible everywhere in the code - tau = gas_model.wall.decomposition_progress(material_densities) - epsilon = gas_model.wall.void_fraction(tau) + wdv = gas_model.wall.dependent_vars(material_densities) + temperature = gas_model.wall.get_temperature( - cv=cv, material_densities=material_densities, - tseed=temperature_seed, tau=tau, gas_model=gas_model) + cv=cv, wdv=wdv, tseed=temperature_seed, eos=gas_model.eos) - pressure = 1.0/epsilon*gas_model.eos.pressure(cv=cv, temperature=temperature) + pressure = gas_model.wall.get_pressure(cv, wdv, temperature, gas_model.eos) if limiter_func: - cv = limiter_func(cv=cv, wv=material_densities, pressure=pressure, - temperature=temperature, epsilon=epsilon, - dd=limiter_dd) + cv = limiter_func(cv=cv, wdv=wdv, pressure=pressure, + temperature=temperature, dd=limiter_dd) dv = PorousFlowDependentVars( temperature=temperature, @@ -419,17 +416,18 @@ def make_fluid_state(cv, gas_model, ) # ~~~ Modify transport vars to include solid effects + # TODO create a new transport class exclusive for porous media flow? gas_tv = gas_model.transport.transport_vars(cv=cv, dv=dv, eos=gas_model.eos) tv = GasTransportVars( bulk_viscosity=( gas_tv.bulk_viscosity), viscosity=gas_model.wall.viscosity( - temperature, tau, gas_tv), + wdv, temperature, gas_tv), thermal_conductivity=gas_model.wall.thermal_conductivity( - cv, material_densities, temperature, tau, gas_tv), + cv, wdv, temperature, gas_tv), species_diffusivity=gas_model.wall.species_diffusivity( - temperature, tau, gas_tv), + wdv, temperature, gas_tv), ) return ViscousFluidState(cv=cv, dv=dv, tv=tv) diff --git a/mirgecom/materials/carbon_fiber.py b/mirgecom/materials/carbon_fiber.py index 39fcb3da3..0a49eae3b 100644 --- a/mirgecom/materials/carbon_fiber.py +++ b/mirgecom/materials/carbon_fiber.py @@ -28,11 +28,11 @@ THE SOFTWARE. """ -import numpy as np from typing import Optional from abc import abstractmethod +import numpy as np from meshmode.dof_array import DOFArray -from mirgecom.wall_model import PorousWallDegradationModel +from mirgecom.wall_model import PorousWallProperties class Oxidation: @@ -106,11 +106,10 @@ def get_source_terms(self, temperature, tau, rhoY_o2) -> DOFArray: # noqa N803 return (mw_co/mw_o2 + mw_o/mw_o2 - 1)*rhoY_o2*k*eff_surf_area -class SolidProperties(PorousWallDegradationModel): +class SolidProperties(PorousWallProperties): """Evaluate the properties of the solid state containing only fibers. .. automethod:: void_fraction - .. automethod:: decomposition_progress .. automethod:: enthalpy .. automethod:: heat_capacity .. automethod:: thermal_conductivity @@ -118,6 +117,7 @@ class SolidProperties(PorousWallDegradationModel): .. automethod:: permeability .. automethod:: emissivity .. automethod:: tortuosity + .. automethod:: decomposition_progress """ def __init__(self, char_mass, virgin_mass): @@ -134,10 +134,6 @@ def void_fraction(self, tau: DOFArray) -> DOFArray: """ return 1.0 - self.volume_fraction(tau) - def decomposition_progress(self, mass: DOFArray) -> DOFArray: - r"""Evaluate the mass loss progress ratio $\tau$ of the oxidation.""" - return 1.0 - (self._virgin_mass - mass)/self._virgin_mass - def enthalpy(self, temperature: DOFArray, tau: Optional[DOFArray]) -> DOFArray: r"""Evaluate the solid enthalpy $h_s$ of the fibers.""" return ( @@ -203,3 +199,12 @@ def tortuosity(self, tau: DOFArray) -> DOFArray: # FIXME find a relation to make it change as a function of "tau" actx = tau.array_context return 1.1 + actx.np.zeros_like(tau) + + def decomposition_progress(self, mass: DOFArray) -> DOFArray: + r"""Evaluate the mass loss progress ratio $\tau$ of the oxidation. + + Where $\tau=1$, the material is locally virgin. On the other hand, if + $\tau=0$, then the oxidation is locally complete and the fibers were + all consumed. + """ + return 1.0 - (self._virgin_mass - mass)/self._virgin_mass diff --git a/mirgecom/materials/tacot.py b/mirgecom/materials/tacot.py index 03f4599d8..e24e532e7 100644 --- a/mirgecom/materials/tacot.py +++ b/mirgecom/materials/tacot.py @@ -35,10 +35,10 @@ import numpy as np from meshmode.dof_array import DOFArray from pytools.obj_array import make_obj_array -from mirgecom.wall_model import PorousWallDegradationModel +from mirgecom.wall_model import PorousWallProperties -# TODO generalize? keep in the driver? +# TODO generalize? define only abstract class and keep this in the driver? class Pyrolysis: r"""Evaluate the source terms for the pyrolysis decomposition. @@ -99,7 +99,7 @@ def get_source_terms(self, temperature, chi): actx.np.zeros_like(temperature)]) -class SolidProperties(PorousWallDegradationModel): +class SolidProperties(PorousWallProperties): """Evaluate the properties of the solid state containing resin and fibers. A linear weighting between the virgin and chared states is applied to @@ -107,7 +107,6 @@ class SolidProperties(PorousWallDegradationModel): interpolation and they are not valid for temperatures above 3200K. .. automethod:: void_fraction - .. automethod:: decomposition_progress .. automethod:: enthalpy .. automethod:: heat_capacity .. automethod:: thermal_conductivity @@ -115,6 +114,7 @@ class SolidProperties(PorousWallDegradationModel): .. automethod:: permeability .. automethod:: emissivity .. automethod:: tortuosity + .. automethod:: decomposition_progress """ def __init__(self, char_mass, virgin_mass): @@ -134,21 +134,6 @@ def void_fraction(self, tau: DOFArray) -> DOFArray: """ return 1.0 - self.volume_fraction(tau) - def decomposition_progress(self, mass: DOFArray) -> DOFArray: - r"""Evaluate the progress ratio $\tau$ of the phenolics decomposition. - - Where $\tau=1$, the material is locally virgin. On the other hand, if - $\tau=0$, then the pyrolysis is locally complete and only charred - material exists: - - .. math:: - \tau = \frac{\rho_0}{\rho_0 - \rho_c} - \left( 1 - \frac{\rho_c}{\rho(t)} \right) - """ - char_mass = self._char_mass - virgin_mass = self._virgin_mass - return virgin_mass/(virgin_mass - char_mass)*(1.0 - char_mass/mass) - def enthalpy(self, temperature: DOFArray, tau: DOFArray) -> DOFArray: """Solid enthalpy as a function of pyrolysis progress.""" virgin = ( @@ -220,3 +205,18 @@ def tortuosity(self, tau: DOFArray) -> DOFArray: virgin = 1.2 char = 1.1 return virgin*tau + char*(1.0 - tau) + + def decomposition_progress(self, mass: DOFArray) -> DOFArray: + r"""Evaluate the progress ratio $\tau$ of the phenolics decomposition. + + Where $\tau=1$, the material is locally virgin. On the other hand, if + $\tau=0$, then the pyrolysis is locally complete and only charred + material exists: + + .. math:: + \tau = \frac{\rho_0}{\rho_0 - \rho_c} + \left( 1 - \frac{\rho_c}{\rho(t)} \right) + """ + char_mass = self._char_mass + virgin_mass = self._virgin_mass + return virgin_mass/(virgin_mass - char_mass)*(1.0 - char_mass/mass) diff --git a/mirgecom/wall_model.py b/mirgecom/wall_model.py index d700aa889..4035856eb 100644 --- a/mirgecom/wall_model.py +++ b/mirgecom/wall_model.py @@ -6,10 +6,35 @@ .. autoclass:: SolidWallEOS .. autoclass:: PorousFlowDependentVars .. autoclass:: PorousWallDependentVars -.. autoclass:: PorousWallDegradationModel -.. autoclass:: PorousWallEOS +.. autoclass:: PorousWallProperties +.. autoclass:: PorousFlowEOS """ +__copyright__ = """ +Copyright (C) 2023 University of Illinois Board of Trustees +""" + +__license__ = """ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + + from dataclasses import dataclass from abc import abstractmethod from typing import Union @@ -23,6 +48,7 @@ from mirgecom.fluid import ConservedVars from mirgecom.eos import MixtureDependentVars from mirgecom.transport import GasTransportVars +from mirgecom.eos import GasEOS @with_container_arithmetic(bcast_obj_array=False, @@ -118,6 +144,10 @@ def dependent_vars(self, wv, tseed=None): @dataclass_array_container @dataclass(frozen=True, eq=False) +# FIXME in practice, the density of the wall materials follow a conservation +# equation. To avoid big changes in the code atm, it is squeezed as a +# dependent variable for "state.dv", although it is not. +# TODO if we decide to refactor it, modify this accordingly class PorousFlowDependentVars(MixtureDependentVars): """Dependent variables for the (porous) fluid state. @@ -136,6 +166,7 @@ class PorousWallDependentVars: .. attribute:: void_fraction .. attribute:: emissivity .. attribute:: permeability + .. attribute:: tortuosity .. attribute:: density """ @@ -143,14 +174,14 @@ class PorousWallDependentVars: void_fraction: DOFArray emissivity: DOFArray permeability: DOFArray + tortuosity: DOFArray density: DOFArray -class PorousWallDegradationModel: - """Abstract interface for wall degradation model. +class PorousWallProperties: + """Abstract interface for porous media domains. .. automethod:: void_fraction - .. automethod:: decomposition_progress .. automethod:: enthalpy .. automethod:: heat_capacity .. automethod:: thermal_conductivity @@ -158,6 +189,7 @@ class PorousWallDegradationModel: .. automethod:: permeability .. automethod:: emissivity .. automethod:: tortuosity + .. automethod:: decomposition_progress """ @abstractmethod @@ -165,11 +197,6 @@ def void_fraction(self, tau: DOFArray) -> DOFArray: r"""Void fraction $\epsilon$ filled by gas around the solid.""" raise NotImplementedError() - @abstractmethod - def decomposition_progress(self, mass: DOFArray) -> DOFArray: - r"""Evaluate the progress ratio $\tau$ of the phenolics decomposition.""" - raise NotImplementedError() - @abstractmethod def enthalpy(self, temperature: DOFArray, tau: DOFArray) -> DOFArray: r"""Evaluate the enthalpy $h_s$ of the solid.""" @@ -206,30 +233,34 @@ def tortuosity(self, tau: DOFArray) -> DOFArray: """Tortuosity of the porous material.""" raise NotImplementedError() + @abstractmethod + def decomposition_progress(self, mass: DOFArray) -> DOFArray: + r"""Evaluate the progress ratio $\tau$ of the phenolics decomposition.""" + raise NotImplementedError() + -class PorousWallEOS: - """Interface for porous material degradation models. +class PorousFlowEOS: + """EOS for porous media flows, including degradation models. This class evaluates the variables dependent on the wall decomposition - state and its different parts. For that, the user must supply external - functions in order to consider the different materials employed at wall - (for instance, carbon fibers, graphite, alumina, steel etc). The number - and types of the materials is case-dependent and, hence, must be defined - in the respective simulation driver. + state and the gas permeating the material. .. automethod:: __init__ .. automethod:: solid_density - .. automethod:: decomposition_progress .. automethod:: void_fraction .. automethod:: get_temperature + .. automethod:: get_pressure .. automethod:: enthalpy .. automethod:: heat_capacity .. automethod:: viscosity .. automethod:: thermal_conductivity .. automethod:: species_diffusivity + .. automethod:: decomposition_progress .. automethod:: dependent_vars """ + # TODO create a class that encapsulate the `PorousWallProperties` and + # `GasModel` ??? def __init__(self, wall_material): """ Initialize the model. @@ -255,23 +286,12 @@ def solid_density(self, material_densities) -> DOFArray: return material_densities return sum(material_densities) - def decomposition_progress(self, material_densities) -> DOFArray: - r"""Evaluate the progress ratio $\tau$ of the oxidation. - - Where $\tau=1$, the material is locally virgin. On the other hand, if - $\tau=0$, then the fibers were all consumed. - """ - mass = self.solid_density(material_densities) - return self._material.decomposition_progress(mass) - def void_fraction(self, tau: DOFArray) -> DOFArray: r"""Void fraction $\epsilon$ of the sample filled with gas.""" return self._material.void_fraction(tau) - def get_temperature(self, cv: ConservedVars, - material_densities: Union[DOFArray, np.ndarray], - tseed: DOFArray, tau: DOFArray, gas_model, - niter=3) -> DOFArray: + def get_temperature(self, cv: ConservedVars, wdv: PorousWallDependentVars, + tseed: DOFArray, eos: GasEOS, niter=3) -> DOFArray: r"""Evaluate the temperature based on solid+gas properties. It uses the assumption of thermal equilibrium between solid and fluid. @@ -285,55 +305,57 @@ def get_temperature(self, cv: ConservedVars, {\epsilon_g \rho_g C_{p_g} + \epsilon_s \rho_s C_{p_s}} """ - actx = cv.array_context - if isinstance(tseed, DOFArray) is False: - temp = tseed + actx.np.zeros_like(tau) + actx = cv.array_context + temp = tseed + actx.np.zeros_like(wdv.tau) else: temp = tseed*1.0 - rho_gas = cv.mass - rho_solid = self.solid_density(material_densities) - - rhoe = cv.energy - 0.5/cv.mass*np.dot(cv.momentum, cv.momentum) + internal_energy = cv.energy - 0.5/cv.mass*np.dot(cv.momentum, cv.momentum) for _ in range(0, niter): gas_internal_energy = \ - gas_model.eos.get_internal_energy(temp, cv.species_mass_fractions) + eos.get_internal_energy(temp, cv.species_mass_fractions) - gas_heat_capacity_cv = gas_model.eos.heat_capacity_cv(cv, temp) + gas_heat_capacity_cv = eos.heat_capacity_cv(cv, temp) - eps_rho_e = rho_gas*gas_internal_energy \ - + rho_solid*self.enthalpy(temp, tau) + eps_rho_e = cv.mass*gas_internal_energy \ + + wdv.density*self._material.enthalpy(temp, wdv.tau) - bulk_cp = rho_gas*gas_heat_capacity_cv \ - + rho_solid*self.heat_capacity(temp, tau) + bulk_cp = cv.mass*gas_heat_capacity_cv \ + + wdv.density*self._material.heat_capacity(temp, wdv.tau) - temp = temp - (eps_rho_e - rhoe)/bulk_cp + temp = temp - (eps_rho_e - internal_energy)/bulk_cp return temp + def get_pressure(self, cv: ConservedVars, wdv: PorousWallDependentVars, + temperature: DOFArray, eos: GasEOS) -> DOFArray: + """Return the pressure of the gas considering the void fraction.""" + return eos.pressure(cv, temperature)/wdv.void_fraction + + # make it return the enthalpy of gas+solid? def enthalpy(self, temperature: DOFArray, tau: DOFArray): - """Return the enthalpy of the test material as a function of temperature.""" + """Return the enthalpy of the test material.""" return self._material.enthalpy(temperature, tau) + # make it return the heat capacity of gas+solid? def heat_capacity(self, temperature: DOFArray, tau: DOFArray) -> DOFArray: """Return the heat capacity of the test material.""" return self._material.heat_capacity(temperature, tau) # TODO: maybe create a WallTransportVars? - def viscosity(self, temperature: DOFArray, tau: DOFArray, + def viscosity(self, wdv: PorousWallDependentVars, temperature: DOFArray, gas_tv: GasTransportVars) -> DOFArray: """Viscosity of the gas through the (porous) wall.""" - epsilon = self._material.void_fraction(tau) - return gas_tv.viscosity/epsilon + return gas_tv.viscosity/wdv.void_fraction # TODO: maybe create a WallTransportVars? def thermal_conductivity(self, cv: ConservedVars, - material_densities: Union[DOFArray, np.ndarray], - temperature: DOFArray, tau: DOFArray, - gas_tv: GasTransportVars): + wdv: PorousWallDependentVars, + temperature: DOFArray, + gas_tv: GasTransportVars) -> DOFArray: r"""Return the effective thermal conductivity of the gas+solid. It is a function of temperature and degradation progress. As the @@ -344,31 +366,28 @@ def thermal_conductivity(self, cv: ConservedVars, .. math:: \frac{\rho_s \kappa_s + \rho_g \kappa_g}{\rho_s + \rho_g} - - Returns - ------- - thermal_conductivity: meshmode.dof_array.DOFArray - the thermal_conductivity, including all parts of the solid """ - y_g = cv.mass/(cv.mass + self.solid_density(material_densities)) + y_g = cv.mass/(cv.mass + wdv.density) y_s = 1.0 - y_g - kappa_s = self._material.thermal_conductivity(temperature, tau) + kappa_s = self._material.thermal_conductivity(temperature, wdv.tau) kappa_g = gas_tv.thermal_conductivity return y_s*kappa_s + y_g*kappa_g # TODO: maybe create a WallTransportVars? - def species_diffusivity(self, temperature: DOFArray, tau: DOFArray, - gas_tv: GasTransportVars): - """Mass diffusivity of gaseous species through the (porous) wall. + def species_diffusivity(self, wdv: PorousWallDependentVars, + temperature: DOFArray, gas_tv: GasTransportVars) -> DOFArray: + """Mass diffusivity of gaseous species through the (porous) wall.""" + return gas_tv.species_diffusivity/wdv.tortuosity - Returns - ------- - species_diffusivity: meshmode.dof_array.DOFArray - the species mass diffusivity inside the wall + def decomposition_progress(self, material_densities) -> DOFArray: + r"""Evaluate the progress ratio $\tau$ of the decomposition. + + Where $\tau=1$, the material is locally virgin. On the other hand, if + $\tau=0$, then the fibers were all consumed. """ - tortuosity = self._material.tortuosity(tau) - return gas_tv.species_diffusivity/tortuosity + mass = self.solid_density(material_densities) + return self._material.decomposition_progress(mass) def dependent_vars(self, material_densities: Union[DOFArray, np.ndarray]): """Get the state-dependent variables. @@ -385,5 +404,6 @@ def dependent_vars(self, material_densities: Union[DOFArray, np.ndarray]): void_fraction=self.void_fraction(tau), emissivity=self._material.emissivity(tau), permeability=self._material.permeability(tau), + tortuosity=self._material.tortuosity(tau), density=self.solid_density(material_densities) ) From c598d7817617c0bb7fe3f93d44b4ae063b9e2550 Mon Sep 17 00:00:00 2001 From: Tulio Date: Wed, 19 Jul 2023 16:58:29 -0500 Subject: [PATCH 08/45] Fix the AW example --- examples/ablation-workshop-mpi.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/ablation-workshop-mpi.py b/examples/ablation-workshop-mpi.py index 18f0e045a..8223802b8 100644 --- a/examples/ablation-workshop-mpi.py +++ b/examples/ablation-workshop-mpi.py @@ -405,7 +405,7 @@ class WallTabulatedEOS(PorousFlowEOS): for TACOT-tabulated data. """ - def get_temperature(self, cv, wdv, tseed, gas_model, niter=3): + def get_temperature(self, cv, wdv, tseed, eos, niter=3): r"""Evaluate the temperature based on solid+gas properties. It uses the assumption of thermal equilibrium between solid and fluid. @@ -443,8 +443,8 @@ def get_temperature(self, cv, wdv, tseed, gas_model, niter=3): The temperature of the gas+solid """ - actx = cv.array_context if isinstance(tseed, DOFArray) is False: + actx = cv.array_context temp = tseed + actx.np.zeros_like(cv.mass) else: temp = tseed*1.0 @@ -452,12 +452,12 @@ def get_temperature(self, cv, wdv, tseed, gas_model, niter=3): for _ in range(0, niter): eps_rho_e = ( - cv.mass*gas_model.eos.get_internal_energy(temp) - + wdv.density*self._material.enthalpy(temp, wdv.tau)) + cv.mass*eos.get_internal_energy(temp) + + wdv.density*self.enthalpy(temp, wdv.tau)) bulk_cp = ( - cv.mass*gas_model.eos.heat_capacity_cv(temp) - + wdv.density*self._material.heat_capacity(temp, wdv.tau)) + cv.mass*eos.heat_capacity_cv(temp) + + wdv.density*self.heat_capacity(temp, wdv.tau)) temp = temp - (eps_rho_e - cv.energy)/bulk_cp @@ -628,7 +628,7 @@ def my_wall_bdry(u_minus): istep = 0 first_step = 0 cv = initializer(dim=dim, gas_model=gas_model, - material_densities=material_densities, material=my_material, + material_densities=material_densities, pressure=pressure, temperature=temperature) # stand-alone version of the "gas_model" to bypass some unnecessary @@ -646,10 +646,10 @@ def make_state(cv, temperature_seed, material_densities): wdv = gas_model.wall.dependent_vars(material_densities) temperature = gas_model.wall.get_temperature( - cv=cv, wdv=wdv, tseed=temperature_seed, gas_model=gas_model) + cv=cv, wdv=wdv, tseed=temperature_seed, eos=gas_model.eos) pressure = gas_model.wall.get_pressure( - cv=cv, wdv=wdv, temperature=temperature, gas_model=gas_model) + cv=cv, wdv=wdv, temperature=temperature, eos=gas_model.eos) dv = PorousFlowDependentVars( temperature=temperature, From 24b361f71447a443b9dc7c74731d33376509a550 Mon Sep 17 00:00:00 2001 From: Tulio Date: Wed, 19 Jul 2023 17:06:52 -0500 Subject: [PATCH 09/45] Change timestep in AW example --- examples/ablation-workshop-mpi.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/ablation-workshop-mpi.py b/examples/ablation-workshop-mpi.py index 8223802b8..6a3af1a44 100644 --- a/examples/ablation-workshop-mpi.py +++ b/examples/ablation-workshop-mpi.py @@ -503,13 +503,13 @@ def main(actx_class=None, use_logmgr=True, casename=None, restart_file=None): viz_path = "viz_data/" vizname = viz_path+casename - t_final = 1.0e-7 + t_final = 4.0e-6 dim = 1 order = 2 - dt = 1.0e-8 - pressure_scaling_factor = 1.0 # noqa N806 + dt = 4.0e-8 + pressure_scaling_factor = 0.1 # noqa N806 nviz = 200 ngarbage = 50 From 36bd8d6a85f07c00537ae8afa9d64314c4485f49 Mon Sep 17 00:00:00 2001 From: Tulio Date: Thu, 20 Jul 2023 20:07:49 -0500 Subject: [PATCH 10/45] Start splitting wall_model from gas_mode; --- examples/ablation-workshop-mpi.py | 72 ++++++++------ mirgecom/gas_model.py | 25 ++--- mirgecom/materials/initializer.py | 6 +- mirgecom/wall_model.py | 160 ++++++++++++------------------ 4 files changed, 119 insertions(+), 144 deletions(-) diff --git a/examples/ablation-workshop-mpi.py b/examples/ablation-workshop-mpi.py index 6a3af1a44..229894fc4 100644 --- a/examples/ablation-workshop-mpi.py +++ b/examples/ablation-workshop-mpi.py @@ -77,11 +77,10 @@ from mirgecom.wall_model import ( PorousFlowDependentVars, PorousWallDependentVars, - PorousFlowEOS + PorousFlowModel ) from mirgecom.fluid import ConservedVars from mirgecom.transport import GasTransportVars -from mirgecom.gas_model import GasModel from logpyle import IntervalTimer, set_dt @@ -152,19 +151,19 @@ def initializer(dim, gas_model, material_densities, temperature, actx = temperature.array_context - tau = gas_model.wall.decomposition_progress(material_densities) + tau = gas_model.decomposition_progress(material_densities) gas_const = 8314.46261815324/gas_model.eos.molar_mass(temperature) if gas_density is None: - eps_gas = gas_model.wall.void_fraction(tau) + eps_gas = gas_model.void_fraction(tau) eps_rho_gas = eps_gas*pressure/(gas_const*temperature) # internal energy (kinetic energy is neglected) eps_rho_solid = sum(material_densities) bulk_energy = ( eps_rho_gas*gas_model.eos.get_internal_energy(temperature) - + eps_rho_solid*gas_model.wall.enthalpy(temperature, tau) + + eps_rho_solid*gas_model.wall_model.enthalpy(temperature, tau) ) zeros = actx.np.zeros_like(tau) @@ -398,14 +397,36 @@ def pressure(self, cv: ConservedVars, temperature: DOFArray) -> DOFArray: return cv.mass*gas_const*temperature -class WallTabulatedEOS(PorousFlowEOS): +class WallTabulatedModel(PorousFlowModel): """EOS for wall using tabulated data. Inherits WallEOS and add an temperature-evaluation function exclusive for TACOT-tabulated data. """ - def get_temperature(self, cv, wdv, tseed, eos, niter=3): + def molar_mass(self, temperature): + return self.eos.molar_mass(temperature) + + def internal_energy(self, cv, wdv, temperature) -> DOFArray: + r"""Evaluate the gas internal energy $e$. + + It is evaluated based on the tabulated enthalpy and molar mass as + + .. math:: + e(T) = h(T) - \frac{R}{M} T + """ + gas_const = 8314.46261815324/self.molar_mass(temperature) + gas_int_energy = self.eos.enthalpy(temperature) - gas_const*temperature + + return (cv.mass*gas_int_energy + + wdv.density*self.wall_model.enthalpy(temperature, wdv.tau)) + + def heat_capacity(self, cv, wdv, temperature) -> DOFArray: + r"""Evaluate the gas internal energy $e$.""" + return (cv.mass*self.eos.heat_capacity_cv(temperature) + + wdv.density*self.wall_model.heat_capacity(temperature, wdv.tau)) + + def get_temperature(self, cv, wdv, tseed, niter=3): r"""Evaluate the temperature based on solid+gas properties. It uses the assumption of thermal equilibrium between solid and fluid. @@ -450,15 +471,8 @@ def get_temperature(self, cv, wdv, tseed, eos, niter=3): temp = tseed*1.0 for _ in range(0, niter): - - eps_rho_e = ( - cv.mass*eos.get_internal_energy(temp) - + wdv.density*self.enthalpy(temp, wdv.tau)) - - bulk_cp = ( - cv.mass*eos.heat_capacity_cv(temp) - + wdv.density*self.heat_capacity(temp, wdv.tau)) - + eps_rho_e = self.internal_energy(cv, wdv, temp) + bulk_cp = self.heat_capacity(cv, wdv, temp) temp = temp - (eps_rho_e - cv.energy)/bulk_cp return temp @@ -593,11 +607,11 @@ def my_wall_bdry(u_minus): bprime_class = BprimeTable() my_material = my_composite.SolidProperties(char_mass=220.0, virgin_mass=280.0) pyrolysis = my_composite.Pyrolysis() - wall_model = WallTabulatedEOS(wall_material=my_material) # }}} - gas_model = GasModel(eos=my_gas, wall=wall_model) + gas_model = WallTabulatedModel(eos=my_gas, wall_model=my_material, + transport=None) # ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -643,13 +657,13 @@ def make_state(cv, temperature_seed, material_densities): """ zeros = actx.np.zeros_like(cv.mass) - wdv = gas_model.wall.dependent_vars(material_densities) + wdv = gas_model.dependent_vars(material_densities) - temperature = gas_model.wall.get_temperature( - cv=cv, wdv=wdv, tseed=temperature_seed, eos=gas_model.eos) + temperature = gas_model.get_temperature( + cv=cv, wdv=wdv, tseed=temperature_seed) - pressure = gas_model.wall.get_pressure( - cv=cv, wdv=wdv, temperature=temperature, eos=gas_model.eos) + pressure = gas_model.get_pressure( + cv=cv, wdv=wdv, temperature=temperature) dv = PorousFlowDependentVars( temperature=temperature, @@ -670,10 +684,10 @@ def make_state(cv, temperature_seed, material_densities): species_diffusivity=cv.species_mass) # coupled solid-gas thermal conductivity - kappa = wall_model.thermal_conductivity(cv, wdv, temperature, gas_tv) + kappa = gas_model.thermal_conductivity(cv, wdv, temperature, gas_tv) # coupled solid-gas viscosity - viscosity = wall_model.viscosity(wdv, temperature, gas_tv) + viscosity = gas_model.viscosity(wdv, temperature, gas_tv) # return modified transport vars tv = GasTransportVars( @@ -804,7 +818,7 @@ def ablation_workshop_flux(dcoll, state, gas_model, velocity, bprime_class, cv = state.cv dv = state.dv - wdv = gas_model.wall.dependent_vars(dv.material_densities) + wdv = gas_model.dependent_vars(dv.material_densities) actx = cv.mass.array_context @@ -883,7 +897,7 @@ def phenolics_operator(dcoll, fluid_state, boundaries, gas_model, pyrolysis, dv = fluid_state.dv tv = fluid_state.tv - wdv = gas_model.wall.dependent_vars(dv.material_densities) + wdv = gas_model.dependent_vars(dv.material_densities) actx = cv.array_context zeros = actx.np.zeros_like(wdv.tau) @@ -891,7 +905,7 @@ def phenolics_operator(dcoll, fluid_state, boundaries, gas_model, pyrolysis, pressure_boundaries, velocity_boundaries = boundaries # pressure diffusivity for Darcy flow - pressure_diffusivity = gas_model.wall.pressure_diffusivity( + pressure_diffusivity = gas_model.pressure_diffusivity( cv, wdv, tv.viscosity) # ~~~~~ @@ -965,7 +979,7 @@ def my_write_viz(step, t, state): cv = state.cv dv = state.dv - wdv = gas_model.wall.dependent_vars(dv.material_densities) + wdv = gas_model.dependent_vars(dv.material_densities) viz_fields = [("CV_density", cv.mass), ("CV_energy", cv.energy), diff --git a/mirgecom/gas_model.py b/mirgecom/gas_model.py index 0e1ef6b96..df492b4f8 100644 --- a/mirgecom/gas_model.py +++ b/mirgecom/gas_model.py @@ -71,10 +71,6 @@ TransportModel, GasTransportVars ) -from mirgecom.wall_model import ( - PorousFlowEOS, - PorousFlowDependentVars -) from mirgecom.utils import normalize_boundaries @@ -90,16 +86,10 @@ class GasModel: A gas transport model to provide transport properties. None for inviscid models. - - .. attribute:: wall - - A wall model to provide properties of the solid for porous media flow. - None for pure-fluid flows. """ eos: GasEOS transport: Optional[TransportModel] = None - wall: Optional[PorousFlowEOS] = None @dataclass_array_container @@ -393,17 +383,18 @@ def make_fluid_state(cv, gas_model, # ~~~ we need to squeeze wall_model in gas_model because this is easily # accessible everywhere in the code - wdv = gas_model.wall.dependent_vars(material_densities) + wdv = gas_model.dependent_vars(material_densities) - temperature = gas_model.wall.get_temperature( - cv=cv, wdv=wdv, tseed=temperature_seed, eos=gas_model.eos) + temperature = gas_model.get_temperature( + cv=cv, wdv=wdv, tseed=temperature_seed) - pressure = gas_model.wall.get_pressure(cv, wdv, temperature, gas_model.eos) + pressure = gas_model.get_pressure(cv, wdv, temperature) if limiter_func: cv = limiter_func(cv=cv, wdv=wdv, pressure=pressure, temperature=temperature, dd=limiter_dd) + from mirgecom.wall_model import PorousFlowDependentVars dv = PorousFlowDependentVars( temperature=temperature, pressure=pressure, @@ -422,11 +413,11 @@ def make_fluid_state(cv, gas_model, tv = GasTransportVars( bulk_viscosity=( gas_tv.bulk_viscosity), - viscosity=gas_model.wall.viscosity( + viscosity=gas_model.viscosity( wdv, temperature, gas_tv), - thermal_conductivity=gas_model.wall.thermal_conductivity( + thermal_conductivity=gas_model.thermal_conductivity( cv, wdv, temperature, gas_tv), - species_diffusivity=gas_model.wall.species_diffusivity( + species_diffusivity=gas_model.species_diffusivity( wdv, temperature, gas_tv), ) diff --git a/mirgecom/materials/initializer.py b/mirgecom/materials/initializer.py index 728f823e8..086da87ee 100644 --- a/mirgecom/materials/initializer.py +++ b/mirgecom/materials/initializer.py @@ -63,16 +63,16 @@ def __call__(self, actx, x_vec, gas_model): temperature = self._temp + zeros species_mass_frac = self._y + zeros - tau = gas_model.wall.decomposition_progress(self._wall_density) + tau = gas_model.decomposition_progress(self._wall_density) - eps_gas = gas_model.wall.void_fraction(tau) + eps_gas = gas_model.wall_model.void_fraction(tau) eps_rho_gas = eps_gas*gas_model.eos.get_density(pressure, temperature, species_mass_frac) # internal energy (kinetic energy is neglected) eps_rho_solid = sum(self._wall_density) bulk_energy = ( - eps_rho_solid*gas_model.wall.enthalpy(temperature, tau) + eps_rho_solid*gas_model.wall_model.enthalpy(temperature, tau) + eps_rho_gas*gas_model.eos.get_internal_energy(temperature, species_mass_frac) ) diff --git a/mirgecom/wall_model.py b/mirgecom/wall_model.py index 4035856eb..f0437318c 100644 --- a/mirgecom/wall_model.py +++ b/mirgecom/wall_model.py @@ -37,7 +37,7 @@ from dataclasses import dataclass from abc import abstractmethod -from typing import Union +from typing import Union, Optional import numpy as np from meshmode.dof_array import DOFArray from arraycontext import ( @@ -45,10 +45,13 @@ with_container_arithmetic, get_container_context_recursively ) +from mirgecom.transport import TransportModel from mirgecom.fluid import ConservedVars -from mirgecom.eos import MixtureDependentVars +from mirgecom.eos import ( + MixtureDependentVars, + GasEOS +) from mirgecom.transport import GasTransportVars -from mirgecom.eos import GasEOS @with_container_arithmetic(bcast_obj_array=False, @@ -142,42 +145,6 @@ def dependent_vars(self, wv, tseed=None): temperature=temperature) -@dataclass_array_container -@dataclass(frozen=True, eq=False) -# FIXME in practice, the density of the wall materials follow a conservation -# equation. To avoid big changes in the code atm, it is squeezed as a -# dependent variable for "state.dv", although it is not. -# TODO if we decide to refactor it, modify this accordingly -class PorousFlowDependentVars(MixtureDependentVars): - """Dependent variables for the (porous) fluid state. - - .. attribute:: material_densities - """ - - material_densities: Union[DOFArray, np.ndarray] - - -@dataclass_array_container -@dataclass(frozen=True, eq=False) -class PorousWallDependentVars: - """Dependent variables for the wall degradation model. - - .. attribute:: tau - .. attribute:: void_fraction - .. attribute:: emissivity - .. attribute:: permeability - .. attribute:: tortuosity - .. attribute:: density - """ - - tau: DOFArray - void_fraction: DOFArray - emissivity: DOFArray - permeability: DOFArray - tortuosity: DOFArray - density: DOFArray - - class PorousWallProperties: """Abstract interface for porous media domains. @@ -239,38 +206,48 @@ def decomposition_progress(self, mass: DOFArray) -> DOFArray: raise NotImplementedError() -class PorousFlowEOS: - """EOS for porous media flows, including degradation models. +@dataclass_array_container +@dataclass(frozen=True, eq=False) +# FIXME in practice, the density of the wall materials follow a conservation +# equation. To avoid big changes in the code atm, it is squeezed as a +# dependent variable for "state.dv", although it is not. +# TODO if we decide to refactor it, modify this accordingly +class PorousFlowDependentVars(MixtureDependentVars): + """Dependent variables for the (porous) fluid state. - This class evaluates the variables dependent on the wall decomposition - state and the gas permeating the material. + .. attribute:: material_densities + """ - .. automethod:: __init__ - .. automethod:: solid_density - .. automethod:: void_fraction - .. automethod:: get_temperature - .. automethod:: get_pressure - .. automethod:: enthalpy - .. automethod:: heat_capacity - .. automethod:: viscosity - .. automethod:: thermal_conductivity - .. automethod:: species_diffusivity - .. automethod:: decomposition_progress - .. automethod:: dependent_vars + material_densities: Union[DOFArray, np.ndarray] + + +@dataclass_array_container +@dataclass(frozen=True, eq=False) +class PorousWallDependentVars: + """Dependent variables for the wall degradation model. + + .. attribute:: tau + .. attribute:: void_fraction + .. attribute:: emissivity + .. attribute:: permeability + .. attribute:: tortuosity + .. attribute:: density """ - # TODO create a class that encapsulate the `PorousWallProperties` and - # `GasModel` ??? - def __init__(self, wall_material): - """ - Initialize the model. + tau: DOFArray + void_fraction: DOFArray + emissivity: DOFArray + permeability: DOFArray + tortuosity: DOFArray + density: DOFArray - Parameters - ---------- - wall_material: - The class with the solid properties of the desired material. - """ - self._material = wall_material + +@dataclass(frozen=True) +class PorousFlowModel: + + wall_model: PorousWallProperties + eos: Optional[GasEOS] = None + transport: Optional[TransportModel] = None def solid_density(self, material_densities) -> DOFArray: r"""Return the solid density $\epsilon_s \rho_s$. @@ -288,10 +265,10 @@ def solid_density(self, material_densities) -> DOFArray: def void_fraction(self, tau: DOFArray) -> DOFArray: r"""Void fraction $\epsilon$ of the sample filled with gas.""" - return self._material.void_fraction(tau) + return self.wall_model.void_fraction(tau) def get_temperature(self, cv: ConservedVars, wdv: PorousWallDependentVars, - tseed: DOFArray, eos: GasEOS, niter=3) -> DOFArray: + tseed: DOFArray, niter=3) -> DOFArray: r"""Evaluate the temperature based on solid+gas properties. It uses the assumption of thermal equilibrium between solid and fluid. @@ -314,36 +291,29 @@ def get_temperature(self, cv: ConservedVars, wdv: PorousWallDependentVars, internal_energy = cv.energy - 0.5/cv.mass*np.dot(cv.momentum, cv.momentum) for _ in range(0, niter): - - gas_internal_energy = \ - eos.get_internal_energy(temp, cv.species_mass_fractions) - - gas_heat_capacity_cv = eos.heat_capacity_cv(cv, temp) - - eps_rho_e = cv.mass*gas_internal_energy \ - + wdv.density*self._material.enthalpy(temp, wdv.tau) - - bulk_cp = cv.mass*gas_heat_capacity_cv \ - + wdv.density*self._material.heat_capacity(temp, wdv.tau) - + eps_rho_e = self.internal_energy(cv, wdv, temp) + bulk_cp = self.heat_capacity(cv, wdv, temp) temp = temp - (eps_rho_e - internal_energy)/bulk_cp return temp def get_pressure(self, cv: ConservedVars, wdv: PorousWallDependentVars, - temperature: DOFArray, eos: GasEOS) -> DOFArray: + temperature: DOFArray) -> DOFArray: """Return the pressure of the gas considering the void fraction.""" - return eos.pressure(cv, temperature)/wdv.void_fraction + return self.eos.pressure(cv, temperature)/wdv.void_fraction - # make it return the enthalpy of gas+solid? - def enthalpy(self, temperature: DOFArray, tau: DOFArray): - """Return the enthalpy of the test material.""" - return self._material.enthalpy(temperature, tau) + def internal_energy(self, cv: ConservedVars, wdv: PorousWallDependentVars, + temperature: DOFArray) -> DOFArray: + """Return the enthalpy of the gas+solid material.""" + return (cv.mass*self.eos.get_internal_energy(temperature, + cv.species_mass_fractions) + + wdv.density*self.wall_model.enthalpy(temperature, wdv.tau)) - # make it return the heat capacity of gas+solid? - def heat_capacity(self, temperature: DOFArray, tau: DOFArray) -> DOFArray: - """Return the heat capacity of the test material.""" - return self._material.heat_capacity(temperature, tau) + def heat_capacity(self, cv: ConservedVars, wdv: PorousWallDependentVars, + temperature: DOFArray) -> DOFArray: + """Return the heat capacity of the gas+solid material.""" + return (cv.mass*self.eos.heat_capacity_cv(cv, temperature) + + wdv.density*self.wall_model.heat_capacity(temperature, wdv.tau)) # TODO: maybe create a WallTransportVars? def viscosity(self, wdv: PorousWallDependentVars, temperature: DOFArray, @@ -369,7 +339,7 @@ def thermal_conductivity(self, cv: ConservedVars, """ y_g = cv.mass/(cv.mass + wdv.density) y_s = 1.0 - y_g - kappa_s = self._material.thermal_conductivity(temperature, wdv.tau) + kappa_s = self.wall_model.thermal_conductivity(temperature, wdv.tau) kappa_g = gas_tv.thermal_conductivity return y_s*kappa_s + y_g*kappa_g @@ -387,7 +357,7 @@ def decomposition_progress(self, material_densities) -> DOFArray: $\tau=0$, then the fibers were all consumed. """ mass = self.solid_density(material_densities) - return self._material.decomposition_progress(mass) + return self.wall_model.decomposition_progress(mass) def dependent_vars(self, material_densities: Union[DOFArray, np.ndarray]): """Get the state-dependent variables. @@ -402,8 +372,8 @@ def dependent_vars(self, material_densities: Union[DOFArray, np.ndarray]): return PorousWallDependentVars( tau=tau, void_fraction=self.void_fraction(tau), - emissivity=self._material.emissivity(tau), - permeability=self._material.permeability(tau), - tortuosity=self._material.tortuosity(tau), + emissivity=self.wall_model.emissivity(tau), + permeability=self.wall_model.permeability(tau), + tortuosity=self.wall_model.tortuosity(tau), density=self.solid_density(material_densities) ) From 463f8755cce2cba6aa5267123f60f0a9b088041d Mon Sep 17 00:00:00 2001 From: Tulio Date: Thu, 20 Jul 2023 20:27:18 -0500 Subject: [PATCH 11/45] docs --- doc/operators/composite_materials.rst | 4 +-- mirgecom/wall_model.py | 40 +++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/doc/operators/composite_materials.rst b/doc/operators/composite_materials.rst index f8e06535c..b41b69327 100644 --- a/doc/operators/composite_materials.rst +++ b/doc/operators/composite_materials.rst @@ -5,8 +5,8 @@ Wall Degradation Modeling Model-specific properties ^^^^^^^^^^^^^^^^^^^^^^^^^ - The properties of the materials are defined in specific files. These files - are required by :class:`~mirgecom.wall_model.PorousFlowEOS`. + The properties of the materials are defined in specific files and used by + :class:`~mirgecom.wall_model.PorousWallProperties`. Carbon fiber ------------ diff --git a/mirgecom/wall_model.py b/mirgecom/wall_model.py index f0437318c..131400d40 100644 --- a/mirgecom/wall_model.py +++ b/mirgecom/wall_model.py @@ -3,11 +3,11 @@ .. autoclass:: SolidWallConservedVars .. autoclass:: SolidWallDependentVars .. autoclass:: SolidWallState -.. autoclass:: SolidWallEOS +.. autoclass:: SolidWallModel .. autoclass:: PorousFlowDependentVars .. autoclass:: PorousWallDependentVars .. autoclass:: PorousWallProperties -.. autoclass:: PorousFlowEOS +.. autoclass:: PorousFlowModel """ __copyright__ = """ @@ -91,8 +91,16 @@ class SolidWallState: dv: SolidWallDependentVars -class SolidWallEOS: - """Model for calculating wall quantities for heat conduction only materials.""" +class SolidWallModel: + """Model for calculating wall quantities for heat conduction only materials. + + .. automethod:: density + .. automethod:: heat_capacity + .. automethod:: enthalpy + .. automethod:: thermal_diffusivity + .. automethod:: thermal_conductivity + .. automethod:: eval_temperature + """ def __init__(self, density_func, enthalpy_func, heat_capacity_func, thermal_conductivity_func): @@ -210,8 +218,10 @@ def decomposition_progress(self, mass: DOFArray) -> DOFArray: @dataclass(frozen=True, eq=False) # FIXME in practice, the density of the wall materials follow a conservation # equation. To avoid big changes in the code atm, it is squeezed as a -# dependent variable for "state.dv", although it is not. -# TODO if we decide to refactor it, modify this accordingly +# dependent variable for "state.dv", although it is not a dv per-se. +# TODO maybe create an extra .xv to pass the wall var and make it independent +# of dv/fluid part. +# TODO extend cv to include wall density? class PorousFlowDependentVars(MixtureDependentVars): """Dependent variables for the (porous) fluid state. @@ -244,7 +254,25 @@ class PorousWallDependentVars: @dataclass(frozen=True) class PorousFlowModel: + """ + + .. attribute:: wall_model + .. attribute:: eos + .. attribute:: transport + + .. automethod:: solid_density + .. automethod:: void_fraction + .. automethod:: get_temperature + .. automethod:: get_pressure + .. automethod:: internal_energy + .. automethod:: heat_capacity + .. automethod:: viscosity + .. automethod:: thermal_conductivity + .. automethod:: species_diffusivity + .. automethod:: decomposition_progress + .. automethod:: dependent_vars + """ wall_model: PorousWallProperties eos: Optional[GasEOS] = None transport: Optional[TransportModel] = None From 16a6f3ea86727cf62e86438882fd38765ba668a1 Mon Sep 17 00:00:00 2001 From: Tulio Date: Thu, 20 Jul 2023 20:34:21 -0500 Subject: [PATCH 12/45] cosmetics in AW example --- examples/ablation-workshop-mpi.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/examples/ablation-workshop-mpi.py b/examples/ablation-workshop-mpi.py index 229894fc4..bfc02c7ce 100644 --- a/examples/ablation-workshop-mpi.py +++ b/examples/ablation-workshop-mpi.py @@ -77,7 +77,7 @@ from mirgecom.wall_model import ( PorousFlowDependentVars, PorousWallDependentVars, - PorousFlowModel + PorousFlowModel as OriginalPorousFlowModel ) from mirgecom.fluid import ConservedVars from mirgecom.transport import GasTransportVars @@ -397,7 +397,7 @@ def pressure(self, cv: ConservedVars, temperature: DOFArray) -> DOFArray: return cv.mass*gas_const*temperature -class WallTabulatedModel(PorousFlowModel): +class PorousFlowModel(OriginalPorousFlowModel): """EOS for wall using tabulated data. Inherits WallEOS and add an temperature-evaluation function exclusive @@ -599,7 +599,7 @@ def my_wall_bdry(u_minus): # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - # {{{ Initialize wall model + # {{{ Initialize flow model import mirgecom.materials.tacot as my_composite @@ -608,10 +608,9 @@ def my_wall_bdry(u_minus): my_material = my_composite.SolidProperties(char_mass=220.0, virgin_mass=280.0) pyrolysis = my_composite.Pyrolysis() - # }}} + gas_model = PorousFlowModel(eos=my_gas, wall_model=my_material, transport=None) - gas_model = WallTabulatedModel(eos=my_gas, wall_model=my_material, - transport=None) + # }}} # ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -659,11 +658,10 @@ def make_state(cv, temperature_seed, material_densities): wdv = gas_model.dependent_vars(material_densities) - temperature = gas_model.get_temperature( - cv=cv, wdv=wdv, tseed=temperature_seed) + temperature = gas_model.get_temperature(cv=cv, wdv=wdv, + tseed=temperature_seed) - pressure = gas_model.get_pressure( - cv=cv, wdv=wdv, temperature=temperature) + pressure = gas_model.get_pressure(cv=cv, wdv=wdv, temperature=temperature) dv = PorousFlowDependentVars( temperature=temperature, @@ -905,8 +903,7 @@ def phenolics_operator(dcoll, fluid_state, boundaries, gas_model, pyrolysis, pressure_boundaries, velocity_boundaries = boundaries # pressure diffusivity for Darcy flow - pressure_diffusivity = gas_model.pressure_diffusivity( - cv, wdv, tv.viscosity) + pressure_diffusivity = gas_model.pressure_diffusivity(cv, wdv, tv.viscosity) # ~~~~~ # viscous RHS From 639a38ee8a66ec91bc9a804ac488adf103715e85 Mon Sep 17 00:00:00 2001 From: Tulio Date: Fri, 21 Jul 2023 16:36:38 -0500 Subject: [PATCH 13/45] Include wv; extra changes to gas_model and wall_model --- examples/ablation-workshop-mpi.py | 110 +++++++++---------- mirgecom/gas_model.py | 53 +++++++--- mirgecom/wall_model.py | 168 ++++++++++++++++-------------- 3 files changed, 175 insertions(+), 156 deletions(-) diff --git a/examples/ablation-workshop-mpi.py b/examples/ablation-workshop-mpi.py index bfc02c7ce..31b438824 100644 --- a/examples/ablation-workshop-mpi.py +++ b/examples/ablation-workshop-mpi.py @@ -73,10 +73,10 @@ logmgr_add_cl_device_info, logmgr_add_device_memory_usage ) -from mirgecom.gas_model import ViscousFluidState +from mirgecom.eos import MixtureDependentVars +from mirgecom.gas_model import PorousFlowFluidState from mirgecom.wall_model import ( - PorousFlowDependentVars, - PorousWallDependentVars, + PorousWallVars, PorousFlowModel as OriginalPorousFlowModel ) from mirgecom.fluid import ConservedVars @@ -156,7 +156,7 @@ def initializer(dim, gas_model, material_densities, temperature, gas_const = 8314.46261815324/gas_model.eos.molar_mass(temperature) if gas_density is None: - eps_gas = gas_model.void_fraction(tau) + eps_gas = gas_model.wall_model.void_fraction(tau) eps_rho_gas = eps_gas*pressure/(gas_const*temperature) # internal energy (kinetic energy is neglected) @@ -358,7 +358,7 @@ def heat_capacity_cp(self, temperature: DOFArray) -> DOFArray: bnds = self._cs_enthalpy.x return eval_spline_derivative(temperature, bnds, coeffs) - def heat_capacity_cv(self, temperature: DOFArray) -> DOFArray: + def heat_capacity_cv(self, cv: ConservedVars, temperature: DOFArray) -> DOFArray: r"""Return the gas heat capacity at constant volume $C_{v_g}$.""" return self.heat_capacity_cp(temperature)/self.gamma(temperature) @@ -404,29 +404,7 @@ class PorousFlowModel(OriginalPorousFlowModel): for TACOT-tabulated data. """ - def molar_mass(self, temperature): - return self.eos.molar_mass(temperature) - - def internal_energy(self, cv, wdv, temperature) -> DOFArray: - r"""Evaluate the gas internal energy $e$. - - It is evaluated based on the tabulated enthalpy and molar mass as - - .. math:: - e(T) = h(T) - \frac{R}{M} T - """ - gas_const = 8314.46261815324/self.molar_mass(temperature) - gas_int_energy = self.eos.enthalpy(temperature) - gas_const*temperature - - return (cv.mass*gas_int_energy - + wdv.density*self.wall_model.enthalpy(temperature, wdv.tau)) - - def heat_capacity(self, cv, wdv, temperature) -> DOFArray: - r"""Evaluate the gas internal energy $e$.""" - return (cv.mass*self.eos.heat_capacity_cv(temperature) - + wdv.density*self.wall_model.heat_capacity(temperature, wdv.tau)) - - def get_temperature(self, cv, wdv, tseed, niter=3): + def get_temperature(self, cv, wv, tseed, niter=3): r"""Evaluate the temperature based on solid+gas properties. It uses the assumption of thermal equilibrium between solid and fluid. @@ -445,7 +423,7 @@ def get_temperature(self, cv, wdv, tseed, niter=3): The fluid conserved variables - wdv: PorousWallDependentVars + wv: PorousWallVars Wall properties as a function of decomposition progress @@ -453,10 +431,6 @@ def get_temperature(self, cv, wdv, tseed, niter=3): Temperature to use as a seed for Netwon iteration - gas_model: GasModel - - The class containing the tabulated data for TACOT - Returns ------- temperature: meshmode.dof_array.DOFArray @@ -471,13 +445,15 @@ def get_temperature(self, cv, wdv, tseed, niter=3): temp = tseed*1.0 for _ in range(0, niter): - eps_rho_e = self.internal_energy(cv, wdv, temp) - bulk_cp = self.heat_capacity(cv, wdv, temp) + eps_rho_e = (cv.mass*self.eos.get_internal_energy(temperature=temp) + + wv.density*self.wall_model.enthalpy(temp, wv.tau)) + bulk_cp = (cv.mass*self.eos.heat_capacity_cv(cv=cv, temperature=temp) + + wv.density*self.wall_model.heat_capacity(temp, wv.tau)) temp = temp - (eps_rho_e - cv.energy)/bulk_cp return temp - def pressure_diffusivity(self, cv: ConservedVars, wdv: PorousWallDependentVars, + def pressure_diffusivity(self, cv: ConservedVars, wv: PorousWallVars, viscosity: DOFArray) -> DOFArray: r"""Return the pressure diffusivity for Darcy flow. @@ -487,7 +463,7 @@ def pressure_diffusivity(self, cv: ConservedVars, wdv: PorousWallDependentVars, where $\mu$ is the gas viscosity, $\epsilon_g$ is the void fraction and $\mathbf{K}$ is the permeability matrix. """ - return cv.mass*wdv.permeability/(viscosity*wdv.void_fraction) + return cv.mass*wv.permeability/(viscosity*wv.void_fraction) def binary_sum(ary): @@ -656,14 +632,23 @@ def make_state(cv, temperature_seed, material_densities): """ zeros = actx.np.zeros_like(cv.mass) - wdv = gas_model.dependent_vars(material_densities) + tau = gas_model.decomposition_progress(material_densities) + wv = PorousWallVars( + material_densities=material_densities, + tau=tau, + density=gas_model.solid_density(material_densities), + void_fraction=gas_model.wall_model.void_fraction(tau), + emissivity=gas_model.wall_model.emissivity(tau), + permeability=gas_model.wall_model.permeability(tau), + tortuosity=gas_model.wall_model.tortuosity(tau) + ) - temperature = gas_model.get_temperature(cv=cv, wdv=wdv, + temperature = gas_model.get_temperature(cv=cv, wv=wv, tseed=temperature_seed) - pressure = gas_model.get_pressure(cv=cv, wdv=wdv, temperature=temperature) + pressure = gas_model.get_pressure(cv=cv, wv=wv, temperature=temperature) - dv = PorousFlowDependentVars( + dv = MixtureDependentVars( temperature=temperature, pressure=pressure, speed_of_sound=zeros, @@ -671,7 +656,6 @@ def make_state(cv, temperature_seed, material_densities): smoothness_kappa=zeros, smoothness_beta=zeros, species_enthalpies=cv.species_mass, - material_densities=material_densities ) # gas only transport vars @@ -682,10 +666,10 @@ def make_state(cv, temperature_seed, material_densities): species_diffusivity=cv.species_mass) # coupled solid-gas thermal conductivity - kappa = gas_model.thermal_conductivity(cv, wdv, temperature, gas_tv) + kappa = gas_model.thermal_conductivity(cv, wv, temperature, gas_tv) # coupled solid-gas viscosity - viscosity = gas_model.viscosity(wdv, temperature, gas_tv) + viscosity = gas_model.viscosity(wv, temperature, gas_tv) # return modified transport vars tv = GasTransportVars( @@ -695,7 +679,7 @@ def make_state(cv, temperature_seed, material_densities): species_diffusivity=cv.species_mass ) - return ViscousFluidState(cv=cv, dv=dv, tv=tv) + return PorousFlowFluidState(cv=cv, dv=dv, tv=tv, wv=wv) compiled_make_state = actx.compile(make_state) @@ -815,8 +799,7 @@ def ablation_workshop_flux(dcoll, state, gas_model, velocity, bprime_class, """ cv = state.cv dv = state.dv - - wdv = gas_model.dependent_vars(dv.material_densities) + wv = state.wv actx = cv.mass.array_context @@ -828,7 +811,7 @@ def ablation_workshop_flux(dcoll, state, gas_model, velocity, bprime_class, temperature_bc = op.project(dcoll, dd_wall, dd_bdry_quad, dv.temperature) m_dot_g = op.project(dcoll, dd_wall, dd_bdry_quad, cv.mass*velocity) - emissivity = op.project(dcoll, dd_wall, dd_bdry_quad, wdv.emissivity) + emissivity = op.project(dcoll, dd_wall, dd_bdry_quad, wv.emissivity) m_dot_g = np.dot(m_dot_g, normal_vec) @@ -894,16 +877,15 @@ def phenolics_operator(dcoll, fluid_state, boundaries, gas_model, pyrolysis, cv = fluid_state.cv dv = fluid_state.dv tv = fluid_state.tv - - wdv = gas_model.dependent_vars(dv.material_densities) + wv = fluid_state.wv actx = cv.array_context - zeros = actx.np.zeros_like(wdv.tau) + zeros = actx.np.zeros_like(wv.tau) pressure_boundaries, velocity_boundaries = boundaries # pressure diffusivity for Darcy flow - pressure_diffusivity = gas_model.pressure_diffusivity(cv, wdv, tv.viscosity) + pressure_diffusivity = gas_model.pressure_diffusivity(cv, wv, tv.viscosity) # ~~~~~ # viscous RHS @@ -951,14 +933,14 @@ def phenolics_operator(dcoll, fluid_state, boundaries, gas_model, pyrolysis, # ~~~~~ # decomposition for each component of the resin resin_pyrolysis = pyrolysis.get_source_terms(temperature=dv.temperature, - chi=dv.material_densities) + chi=wv.material_densities) # flip sign due to mass conservation gas_source_term = -pressure_scaling_factor*sum(resin_pyrolysis) # viscous dissipation due to friction inside the porous - visc_diss_energy = tv.viscosity*wdv.void_fraction**2*( - (1.0/wdv.permeability)*np.dot(velocity, velocity)) + visc_diss_energy = tv.viscosity*wv.void_fraction**2*( + (1.0/wv.permeability)*np.dot(velocity, velocity)) source_terms = ConservedVars( mass=gas_source_term, @@ -976,16 +958,21 @@ def my_write_viz(step, t, state): cv = state.cv dv = state.dv - wdv = gas_model.dependent_vars(dv.material_densities) + wv = state.wv viz_fields = [("CV_density", cv.mass), ("CV_energy", cv.energy), ("DV_T", dv.temperature), ("DV_P", dv.pressure), - ("WV_phase_1", dv.material_densities[0]), - ("WV_phase_2", dv.material_densities[1]), - ("WV_phase_3", dv.material_densities[2]), - ("WDV", wdv) + ("WV_phase_1", wv.material_densities[0]), + ("WV_phase_2", wv.material_densities[1]), + ("WV_phase_3", wv.material_densities[2]), + ("WV_tau", wv.tau), + ("WV_void_fraction", wv.void_fraction), + ("WV_emissivity", wv.emissivity), + ("WV_permeability", wv.permeability), + ("WV_tortuosity", wv.tortuosity), + ("WV_density", wv.density), ] # depending on the version, paraview may complain without this @@ -997,12 +984,13 @@ def my_write_viz(step, t, state): def my_write_restart(step, t, state): cv = state.cv dv = state.dv + wv = state.wv rst_fname = rst_pattern.format(cname=casename, step=step, rank=rank) if rst_fname != restart_file: rst_data = { "local_mesh": local_mesh, "cv": cv, - "material_densities": dv.material_densities, + "material_densities": wv.material_densities, "tseed": dv.temperature, "t": t, "step": step, diff --git a/mirgecom/gas_model.py b/mirgecom/gas_model.py index df492b4f8..47e1eb474 100644 --- a/mirgecom/gas_model.py +++ b/mirgecom/gas_model.py @@ -72,6 +72,7 @@ GasTransportVars ) from mirgecom.utils import normalize_boundaries +from mirgecom.wall_model import PorousWallVars, PorousFlowModel @dataclass(frozen=True) @@ -275,6 +276,19 @@ def species_diffusivity(self): return self.tv.species_diffusivity +@dataclass_array_container +@dataclass(frozen=True, eq=False) +class PorousFlowFluidState(ViscousFluidState): + """Dependent variables for the (porous) fluid state. + + .. attribute:: wv + + Porous media flow state-dependent properties. + """ + + wv: PorousWallVars + + def make_fluid_state(cv, gas_model, temperature_seed=None, smoothness_mu=None, @@ -339,9 +353,7 @@ def make_fluid_state(cv, gas_model, smoothness_beta = (actx.np.zeros_like(cv.mass) if smoothness_beta is None else smoothness_beta) - # the porous media is identified if the wall density is not None - - if material_densities is None: + if isinstance(gas_model, GasModel): temperature = gas_model.eos.temperature(cv=cv, temperature_seed=temperature_seed) pressure = gas_model.eos.pressure(cv=cv, temperature=temperature) @@ -377,25 +389,33 @@ def make_fluid_state(cv, gas_model, return FluidState(cv=cv, dv=dv) - else: + if isinstance(gas_model, PorousFlowModel): # FIXME per previous review, think of a way to de-couple wall and fluid. # ~~~ we need to squeeze wall_model in gas_model because this is easily # accessible everywhere in the code - wdv = gas_model.dependent_vars(material_densities) + tau = gas_model.decomposition_progress(material_densities) + wv = PorousWallVars( + material_densities=material_densities, + tau=tau, + density=gas_model.solid_density(material_densities), + void_fraction=gas_model.wall_model.void_fraction(tau), + emissivity=gas_model.wall_model.emissivity(tau), + permeability=gas_model.wall_model.permeability(tau), + tortuosity=gas_model.wall_model.tortuosity(tau) + ) temperature = gas_model.get_temperature( - cv=cv, wdv=wdv, tseed=temperature_seed) + cv=cv, wv=wv, tseed=temperature_seed) - pressure = gas_model.get_pressure(cv, wdv, temperature) + pressure = gas_model.get_pressure(cv, wv, temperature) if limiter_func: - cv = limiter_func(cv=cv, wdv=wdv, pressure=pressure, + cv = limiter_func(cv=cv, wv=wv, pressure=pressure, temperature=temperature, dd=limiter_dd) - from mirgecom.wall_model import PorousFlowDependentVars - dv = PorousFlowDependentVars( + dv = MixtureDependentVars( temperature=temperature, pressure=pressure, speed_of_sound=gas_model.eos.sound_speed(cv, temperature), @@ -403,7 +423,6 @@ def make_fluid_state(cv, gas_model, smoothness_kappa=smoothness_kappa, smoothness_beta=smoothness_beta, species_enthalpies=gas_model.eos.species_enthalpies(cv, temperature), - material_densities=material_densities ) # ~~~ Modify transport vars to include solid effects @@ -414,14 +433,14 @@ def make_fluid_state(cv, gas_model, bulk_viscosity=( gas_tv.bulk_viscosity), viscosity=gas_model.viscosity( - wdv, temperature, gas_tv), + wv, temperature, gas_tv), thermal_conductivity=gas_model.thermal_conductivity( - cv, wdv, temperature, gas_tv), + cv, wv, temperature, gas_tv), species_diffusivity=gas_model.species_diffusivity( - wdv, temperature, gas_tv), + wv, temperature, gas_tv), ) - return ViscousFluidState(cv=cv, dv=dv, tv=tv) + return PorousFlowFluidState(cv=cv, dv=dv, tv=tv, wv=wv) def project_fluid_state(dcoll, src, tgt, state, gas_model, limiter_func=None): @@ -486,7 +505,7 @@ def project_fluid_state(dcoll, src, tgt, state, gas_model, limiter_func=None): smoothness_beta = op.project(dcoll, src, tgt, state.dv.smoothness_beta) material_densities = None - if gas_model.wall is not None: + if isinstance(gas_model, PorousFlowModel): material_densities = op.project(dcoll, src, tgt, state.dv.material_densities) return make_fluid_state(cv=cv_sd, gas_model=gas_model, @@ -740,7 +759,7 @@ def make_operator_fluid_states( tag=(_FluidSmoothnessBetaTag, comm_tag))] material_densities_interior_pairs = None - if gas_model.wall is not None: + if isinstance(gas_model, PorousFlowModel): material_densities_interior_pairs = [ interp_to_surf_quad(tpair=tpair) for tpair in interior_trace_pairs( diff --git a/mirgecom/wall_model.py b/mirgecom/wall_model.py index 131400d40..4042d2e87 100644 --- a/mirgecom/wall_model.py +++ b/mirgecom/wall_model.py @@ -4,8 +4,7 @@ .. autoclass:: SolidWallDependentVars .. autoclass:: SolidWallState .. autoclass:: SolidWallModel -.. autoclass:: PorousFlowDependentVars -.. autoclass:: PorousWallDependentVars +.. autoclass:: PorousWallVars .. autoclass:: PorousWallProperties .. autoclass:: PorousFlowModel """ @@ -99,7 +98,7 @@ class SolidWallModel: .. automethod:: enthalpy .. automethod:: thermal_diffusivity .. automethod:: thermal_conductivity - .. automethod:: eval_temperature + .. automethod:: get_temperature """ def __init__(self, density_func, enthalpy_func, heat_capacity_func, @@ -132,7 +131,7 @@ def thermal_conductivity(self, temperature): """Return the wall thermal conductivity for all components.""" return self._thermal_conductivity_func(temperature) - def eval_temperature(self, wv, tseed=None): + def get_temperature(self, wv, tseed=None): """Evaluate the temperature based on the energy.""" if tseed is not None: temp = tseed*1.0 @@ -216,26 +215,10 @@ def decomposition_progress(self, mass: DOFArray) -> DOFArray: @dataclass_array_container @dataclass(frozen=True, eq=False) -# FIXME in practice, the density of the wall materials follow a conservation -# equation. To avoid big changes in the code atm, it is squeezed as a -# dependent variable for "state.dv", although it is not a dv per-se. -# TODO maybe create an extra .xv to pass the wall var and make it independent -# of dv/fluid part. -# TODO extend cv to include wall density? -class PorousFlowDependentVars(MixtureDependentVars): - """Dependent variables for the (porous) fluid state. +class PorousWallVars: + """Variables for the (porous) fluid state. .. attribute:: material_densities - """ - - material_densities: Union[DOFArray, np.ndarray] - - -@dataclass_array_container -@dataclass(frozen=True, eq=False) -class PorousWallDependentVars: - """Dependent variables for the wall degradation model. - .. attribute:: tau .. attribute:: void_fraction .. attribute:: emissivity @@ -244,6 +227,7 @@ class PorousWallDependentVars: .. attribute:: density """ + material_densities: Union[DOFArray, np.ndarray] tau: DOFArray void_fraction: DOFArray emissivity: DOFArray @@ -252,17 +236,68 @@ class PorousWallDependentVars: density: DOFArray +class PorousFlowEOS: + + @abstractmethod + def decomposition_progress(self, mass: DOFArray) -> DOFArray: + r"""Evaluate the progress ratio $\tau$ of the phenolics decomposition.""" + raise NotImplementedError() + + def solid_density(self, material_densities) -> DOFArray: + r"""Return the solid density $\epsilon_s \rho_s$.""" + raise NotImplementedError() + + def get_temperature(self, cv: ConservedVars, wv: PorousWallVars, + tseed: DOFArray, niter=3) -> DOFArray: + r"""Evaluate the temperature based on solid+gas properties.""" + raise NotImplementedError() + + def get_pressure(self, cv: ConservedVars, wv: PorousWallVars, + temperature: DOFArray) -> DOFArray: + """Return the pressure of the gas considering the void fraction.""" + raise NotImplementedError() + + def internal_energy(self, cv: ConservedVars, wv: PorousWallVars, + temperature: DOFArray) -> DOFArray: + """Return the enthalpy of the gas+solid material.""" + raise NotImplementedError() + + def heat_capacity(self, cv: ConservedVars, wv: PorousWallVars, + temperature: DOFArray) -> DOFArray: + """Return the heat capacity of the gas+solid material.""" + raise NotImplementedError() + + # TODO: maybe create a WallTransportVars? + def viscosity(self, wv: PorousWallVars, temperature: DOFArray, + gas_tv: GasTransportVars) -> DOFArray: + """Viscosity of the gas through the (porous) wall.""" + raise NotImplementedError() + + # TODO: maybe create a WallTransportVars? + def thermal_conductivity(self, cv: ConservedVars, + wv: PorousWallVars, + temperature: DOFArray, + gas_tv: GasTransportVars) -> DOFArray: + r"""Return the effective thermal conductivity of the gas+solid.""" + raise NotImplementedError() + + # TODO: maybe create a WallTransportVars? + def species_diffusivity(self, wv: PorousWallVars, + temperature: DOFArray, gas_tv: GasTransportVars) -> DOFArray: + """Mass diffusivity of gaseous species through the (porous) wall.""" + raise NotImplementedError() + + @dataclass(frozen=True) -class PorousFlowModel: +class PorousFlowModel(PorousFlowEOS): """ - + ... .. attribute:: wall_model .. attribute:: eos .. attribute:: transport - + .. automethod:: solid_density - .. automethod:: void_fraction .. automethod:: get_temperature .. automethod:: get_pressure .. automethod:: internal_energy @@ -271,11 +306,20 @@ class PorousFlowModel: .. automethod:: thermal_conductivity .. automethod:: species_diffusivity .. automethod:: decomposition_progress - .. automethod:: dependent_vars """ + + eos: GasEOS + transport: TransportModel wall_model: PorousWallProperties - eos: Optional[GasEOS] = None - transport: Optional[TransportModel] = None + + def decomposition_progress(self, material_densities) -> DOFArray: + r"""Evaluate the progress ratio $\tau$ of the decomposition. + + Where $\tau=1$, the material is locally virgin. On the other hand, if + $\tau=0$, then the fibers were all consumed. + """ + mass = self.solid_density(material_densities) + return self.wall_model.decomposition_progress(mass) def solid_density(self, material_densities) -> DOFArray: r"""Return the solid density $\epsilon_s \rho_s$. @@ -291,11 +335,7 @@ def solid_density(self, material_densities) -> DOFArray: return material_densities return sum(material_densities) - def void_fraction(self, tau: DOFArray) -> DOFArray: - r"""Void fraction $\epsilon$ of the sample filled with gas.""" - return self.wall_model.void_fraction(tau) - - def get_temperature(self, cv: ConservedVars, wdv: PorousWallDependentVars, + def get_temperature(self, cv: ConservedVars, wv: PorousWallVars, tseed: DOFArray, niter=3) -> DOFArray: r"""Evaluate the temperature based on solid+gas properties. @@ -312,46 +352,46 @@ def get_temperature(self, cv: ConservedVars, wdv: PorousWallDependentVars, """ if isinstance(tseed, DOFArray) is False: actx = cv.array_context - temp = tseed + actx.np.zeros_like(wdv.tau) + temp = tseed + actx.np.zeros_like(wv.tau) else: temp = tseed*1.0 internal_energy = cv.energy - 0.5/cv.mass*np.dot(cv.momentum, cv.momentum) for _ in range(0, niter): - eps_rho_e = self.internal_energy(cv, wdv, temp) - bulk_cp = self.heat_capacity(cv, wdv, temp) + eps_rho_e = self.internal_energy(cv, wv, temp) + bulk_cp = self.heat_capacity(cv, wv, temp) temp = temp - (eps_rho_e - internal_energy)/bulk_cp return temp - def get_pressure(self, cv: ConservedVars, wdv: PorousWallDependentVars, + def get_pressure(self, cv: ConservedVars, wv: PorousWallVars, temperature: DOFArray) -> DOFArray: """Return the pressure of the gas considering the void fraction.""" - return self.eos.pressure(cv, temperature)/wdv.void_fraction + return self.eos.pressure(cv, temperature)/wv.void_fraction - def internal_energy(self, cv: ConservedVars, wdv: PorousWallDependentVars, + def internal_energy(self, cv: ConservedVars, wv: PorousWallVars, temperature: DOFArray) -> DOFArray: """Return the enthalpy of the gas+solid material.""" return (cv.mass*self.eos.get_internal_energy(temperature, cv.species_mass_fractions) - + wdv.density*self.wall_model.enthalpy(temperature, wdv.tau)) + + wv.density*self.wall_model.enthalpy(temperature, wv.tau)) - def heat_capacity(self, cv: ConservedVars, wdv: PorousWallDependentVars, + def heat_capacity(self, cv: ConservedVars, wv: PorousWallVars, temperature: DOFArray) -> DOFArray: """Return the heat capacity of the gas+solid material.""" return (cv.mass*self.eos.heat_capacity_cv(cv, temperature) - + wdv.density*self.wall_model.heat_capacity(temperature, wdv.tau)) + + wv.density*self.wall_model.heat_capacity(temperature, wv.tau)) # TODO: maybe create a WallTransportVars? - def viscosity(self, wdv: PorousWallDependentVars, temperature: DOFArray, + def viscosity(self, wv: PorousWallVars, temperature: DOFArray, gas_tv: GasTransportVars) -> DOFArray: """Viscosity of the gas through the (porous) wall.""" - return gas_tv.viscosity/wdv.void_fraction + return gas_tv.viscosity/wv.void_fraction # TODO: maybe create a WallTransportVars? def thermal_conductivity(self, cv: ConservedVars, - wdv: PorousWallDependentVars, + wv: PorousWallVars, temperature: DOFArray, gas_tv: GasTransportVars) -> DOFArray: r"""Return the effective thermal conductivity of the gas+solid. @@ -365,43 +405,15 @@ def thermal_conductivity(self, cv: ConservedVars, .. math:: \frac{\rho_s \kappa_s + \rho_g \kappa_g}{\rho_s + \rho_g} """ - y_g = cv.mass/(cv.mass + wdv.density) + y_g = cv.mass/(cv.mass + wv.density) y_s = 1.0 - y_g - kappa_s = self.wall_model.thermal_conductivity(temperature, wdv.tau) + kappa_s = self.wall_model.thermal_conductivity(temperature, wv.tau) kappa_g = gas_tv.thermal_conductivity return y_s*kappa_s + y_g*kappa_g # TODO: maybe create a WallTransportVars? - def species_diffusivity(self, wdv: PorousWallDependentVars, + def species_diffusivity(self, wv: PorousWallVars, temperature: DOFArray, gas_tv: GasTransportVars) -> DOFArray: """Mass diffusivity of gaseous species through the (porous) wall.""" - return gas_tv.species_diffusivity/wdv.tortuosity - - def decomposition_progress(self, material_densities) -> DOFArray: - r"""Evaluate the progress ratio $\tau$ of the decomposition. - - Where $\tau=1$, the material is locally virgin. On the other hand, if - $\tau=0$, then the fibers were all consumed. - """ - mass = self.solid_density(material_densities) - return self.wall_model.decomposition_progress(mass) - - def dependent_vars(self, material_densities: Union[DOFArray, np.ndarray]): - """Get the state-dependent variables. - - Returns - ------- - dependent_vars: :class:`PorousWallDependentVars` - dependent variables of the wall-only state. These are complementary - to the fluid dependent variables, but not stacked together. - """ - tau = self.decomposition_progress(material_densities) - return PorousWallDependentVars( - tau=tau, - void_fraction=self.void_fraction(tau), - emissivity=self.wall_model.emissivity(tau), - permeability=self.wall_model.permeability(tau), - tortuosity=self.wall_model.tortuosity(tau), - density=self.solid_density(material_densities) - ) + return gas_tv.species_diffusivity/wv.tortuosity From 9af4794eb4ec2b3dd9747ff35f5a557caa0dffc9 Mon Sep 17 00:00:00 2001 From: Tulio Date: Fri, 21 Jul 2023 17:12:53 -0500 Subject: [PATCH 14/45] flake8; pydocstyle; docs --- doc/operators/composite_materials.rst | 6 +++--- mirgecom/gas_model.py | 9 +++++---- mirgecom/materials/initializer.py | 21 +++++++++++++++++++-- mirgecom/wall_model.py | 23 +++++++++++++---------- 4 files changed, 40 insertions(+), 19 deletions(-) diff --git a/doc/operators/composite_materials.rst b/doc/operators/composite_materials.rst index b41b69327..3fd8dbda7 100644 --- a/doc/operators/composite_materials.rst +++ b/doc/operators/composite_materials.rst @@ -70,7 +70,7 @@ Carbon Fiber Oxidation From the conserved variables, it is possible to compute the oxidation progress, denoted by - :attr:`~mirgecom.wall_model.PorousWallDependentVars.tau`. + :attr:`~mirgecom.wall_model.PorousWallVars.tau`. As a consequence, the instantaneous material properties will change due to the mass loss. @@ -98,7 +98,7 @@ Composite Materials material degradation. As the :class:`~mirgecom.materials.tacot.Pyrolysis` progresses, the mass of each $i$ constituents of the resin, denoted by - :attr:`~mirgecom.wall_model.PorousFlowDependentVars.material_densities`, + :attr:`~mirgecom.wall_model.PorousWallVars.material_densities`, is calculated as .. math :: @@ -148,7 +148,7 @@ Composite Materials From the conserved variables, it is possible to compute the decomposition status, denoted by - :attr:`~mirgecom.wall_model.PorousWallDependentVars.tau`. + :attr:`~mirgecom.wall_model.PorousWallVars.tau`. This yields the proportion of virgin (unpyrolyzed material) to char (fully pyrolyzed) and, consequently, the different thermophysicochemical properties of the solid phase. Thus, the instantaneous material properties diff --git a/mirgecom/gas_model.py b/mirgecom/gas_model.py index 47e1eb474..48ce40105 100644 --- a/mirgecom/gas_model.py +++ b/mirgecom/gas_model.py @@ -442,6 +442,7 @@ def make_fluid_state(cv, gas_model, return PorousFlowFluidState(cv=cv, dv=dv, tv=tv, wv=wv) + return None def project_fluid_state(dcoll, src, tgt, state, gas_model, limiter_func=None): """Project a fluid state onto a boundary consistent with the gas model. @@ -506,7 +507,7 @@ def project_fluid_state(dcoll, src, tgt, state, gas_model, limiter_func=None): material_densities = None if isinstance(gas_model, PorousFlowModel): - material_densities = op.project(dcoll, src, tgt, state.dv.material_densities) + material_densities = op.project(dcoll, src, tgt, state.wv.material_densities) return make_fluid_state(cv=cv_sd, gas_model=gas_model, temperature_seed=temperature_seed, @@ -763,7 +764,7 @@ def make_operator_fluid_states( material_densities_interior_pairs = [ interp_to_surf_quad(tpair=tpair) for tpair in interior_trace_pairs( - dcoll, volume_state.dv.material_densities, volume_dd=dd_vol, + dcoll, volume_state.wv.material_densities, volume_dd=dd_vol, tag=(_WallDensityTag, comm_tag))] interior_boundary_states_quad = make_fluid_state_trace_pairs( @@ -857,8 +858,8 @@ def replace_fluid_state( else state.temperature) material_densities = (None - if gas_model.wall is None - else state.dv.material_densities) + if isinstance(gas_model, PorousFlowModel) + else state.wv.material_densities) return make_fluid_state( cv=new_cv, diff --git a/mirgecom/materials/initializer.py b/mirgecom/materials/initializer.py index 086da87ee..0c14ed4f6 100644 --- a/mirgecom/materials/initializer.py +++ b/mirgecom/materials/initializer.py @@ -39,7 +39,16 @@ class SolidWallInitializer: def __init__(self, temperature): self._temp = temperature - def __call__(self, actx, x_vec, wall_model): + def __call__(self, x_vec, wall_model): + """Evaluate the wall+gas properties for porous materials. + + Parameters + ---------- + x_vec: numpy.ndarray + Nodal coordinates + wall_model: :class:`mirgecom.wall_model.SolidWallModel` + Equation of state class + """ mass = wall_model.density() energy = mass * wall_model.enthalpy(self._temp) return SolidWallConservedVars(mass=mass, energy=energy) @@ -55,8 +64,16 @@ def __init__(self, pressure, temperature, species, material_densities): self._temp = temperature self._wall_density = material_densities - def __call__(self, actx, x_vec, gas_model): + def __call__(self, x_vec, gas_model): + """Evaluate the wall+gas properties for porous materials. + Parameters + ---------- + x_vec: numpy.ndarray + Nodal coordinates + gas_model: :class:`mirgecom.wall_model.PorousFlowModel` + Equation of state class + """ zeros = actx.np.zeros_like(x_vec[0]) pressure = self._pres + zeros diff --git a/mirgecom/wall_model.py b/mirgecom/wall_model.py index 4042d2e87..b08367d12 100644 --- a/mirgecom/wall_model.py +++ b/mirgecom/wall_model.py @@ -36,7 +36,7 @@ from dataclasses import dataclass from abc import abstractmethod -from typing import Union, Optional +from typing import Union import numpy as np from meshmode.dof_array import DOFArray from arraycontext import ( @@ -46,10 +46,7 @@ ) from mirgecom.transport import TransportModel from mirgecom.fluid import ConservedVars -from mirgecom.eos import ( - MixtureDependentVars, - GasEOS -) +from mirgecom.eos import GasEOS from mirgecom.transport import GasTransportVars @@ -145,7 +142,7 @@ def get_temperature(self, wv, tseed=None): def dependent_vars(self, wv, tseed=None): """Return solid wall dependent variables.""" - temperature = self.eval_temperature(wv, tseed) + temperature = self.get_temperature(wv, tseed) kappa = self.thermal_conductivity(temperature) return SolidWallDependentVars( thermal_conductivity=kappa, @@ -237,43 +234,49 @@ class PorousWallVars: class PorousFlowEOS: + """Abstract interface to equation of porous flow class.""" @abstractmethod - def decomposition_progress(self, mass: DOFArray) -> DOFArray: + def decomposition_progress(self, material_densities) -> DOFArray: r"""Evaluate the progress ratio $\tau$ of the phenolics decomposition.""" raise NotImplementedError() + @abstractmethod def solid_density(self, material_densities) -> DOFArray: r"""Return the solid density $\epsilon_s \rho_s$.""" raise NotImplementedError() + @abstractmethod def get_temperature(self, cv: ConservedVars, wv: PorousWallVars, tseed: DOFArray, niter=3) -> DOFArray: r"""Evaluate the temperature based on solid+gas properties.""" raise NotImplementedError() + @abstractmethod def get_pressure(self, cv: ConservedVars, wv: PorousWallVars, temperature: DOFArray) -> DOFArray: """Return the pressure of the gas considering the void fraction.""" raise NotImplementedError() + @abstractmethod def internal_energy(self, cv: ConservedVars, wv: PorousWallVars, temperature: DOFArray) -> DOFArray: """Return the enthalpy of the gas+solid material.""" raise NotImplementedError() + @abstractmethod def heat_capacity(self, cv: ConservedVars, wv: PorousWallVars, temperature: DOFArray) -> DOFArray: """Return the heat capacity of the gas+solid material.""" raise NotImplementedError() - # TODO: maybe create a WallTransportVars? + @abstractmethod def viscosity(self, wv: PorousWallVars, temperature: DOFArray, gas_tv: GasTransportVars) -> DOFArray: """Viscosity of the gas through the (porous) wall.""" raise NotImplementedError() - # TODO: maybe create a WallTransportVars? + @abstractmethod def thermal_conductivity(self, cv: ConservedVars, wv: PorousWallVars, temperature: DOFArray, @@ -281,7 +284,7 @@ def thermal_conductivity(self, cv: ConservedVars, r"""Return the effective thermal conductivity of the gas+solid.""" raise NotImplementedError() - # TODO: maybe create a WallTransportVars? + @abstractmethod def species_diffusivity(self, wv: PorousWallVars, temperature: DOFArray, gas_tv: GasTransportVars) -> DOFArray: """Mass diffusivity of gaseous species through the (porous) wall.""" From cfed1f83060ef62a07871ce381af776a72ecb2a5 Mon Sep 17 00:00:00 2001 From: Tulio Date: Sat, 22 Jul 2023 09:43:05 -0500 Subject: [PATCH 15/45] Fix wrong conditional --- mirgecom/gas_model.py | 3 ++- mirgecom/materials/initializer.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mirgecom/gas_model.py b/mirgecom/gas_model.py index 48ce40105..54a836181 100644 --- a/mirgecom/gas_model.py +++ b/mirgecom/gas_model.py @@ -444,6 +444,7 @@ def make_fluid_state(cv, gas_model, return None + def project_fluid_state(dcoll, src, tgt, state, gas_model, limiter_func=None): """Project a fluid state onto a boundary consistent with the gas model. @@ -858,7 +859,7 @@ def replace_fluid_state( else state.temperature) material_densities = (None - if isinstance(gas_model, PorousFlowModel) + if isinstance(gas_model, PorousFlowModel) is False else state.wv.material_densities) return make_fluid_state( diff --git a/mirgecom/materials/initializer.py b/mirgecom/materials/initializer.py index 0c14ed4f6..8f744f29e 100644 --- a/mirgecom/materials/initializer.py +++ b/mirgecom/materials/initializer.py @@ -74,6 +74,7 @@ def __call__(self, x_vec, gas_model): gas_model: :class:`mirgecom.wall_model.PorousFlowModel` Equation of state class """ + actx = x_vec.array_context zeros = actx.np.zeros_like(x_vec[0]) pressure = self._pres + zeros From 49c5e17ff46d7f98f01761dc0c3f74455427fdd1 Mon Sep 17 00:00:00 2001 From: Tulio Date: Sat, 22 Jul 2023 13:19:35 -0500 Subject: [PATCH 16/45] Add new transport class for porous flow --- examples/ablation-workshop-mpi.py | 32 +++++++++++++ mirgecom/gas_model.py | 21 ++------ mirgecom/materials/initializer.py | 2 +- mirgecom/transport.py | 80 ++++++++++++++++++++++++++++++- mirgecom/wall_model.py | 61 +---------------------- 5 files changed, 118 insertions(+), 78 deletions(-) diff --git a/examples/ablation-workshop-mpi.py b/examples/ablation-workshop-mpi.py index 31b438824..63d67e792 100644 --- a/examples/ablation-workshop-mpi.py +++ b/examples/ablation-workshop-mpi.py @@ -465,6 +465,38 @@ def pressure_diffusivity(self, cv: ConservedVars, wv: PorousWallVars, """ return cv.mass*wv.permeability/(viscosity*wv.void_fraction) + def viscosity(self, wv: PorousWallVars, temperature: DOFArray, + gas_tv: GasTransportVars) -> DOFArray: + """Viscosity of the gas through the (porous) wall.""" + return gas_tv.viscosity/wv.void_fraction + + def thermal_conductivity(self, cv: ConservedVars, + wv: PorousWallVars, + temperature: DOFArray, + gas_tv: GasTransportVars) -> DOFArray: + r"""Return the effective thermal conductivity of the gas+solid. + + It is a function of temperature and degradation progress. As the + fibers are oxidized, they reduce their cross area and, consequently, + their ability to conduct heat. + + It is evaluated using a mass-weighted average given by + + .. math:: + \frac{\rho_s \kappa_s + \rho_g \kappa_g}{\rho_s + \rho_g} + """ + y_g = cv.mass/(cv.mass + wv.density) + y_s = 1.0 - y_g + kappa_s = self.wall_model.thermal_conductivity(temperature, wv.tau) + kappa_g = gas_tv.thermal_conductivity + + return y_s*kappa_s + y_g*kappa_g + + def species_diffusivity(self, wv: PorousWallVars, + temperature: DOFArray, gas_tv: GasTransportVars) -> DOFArray: + """Mass diffusivity of gaseous species through the (porous) wall.""" + return gas_tv.species_diffusivity/wv.tortuosity + def binary_sum(ary): """Sum the elements of an array, creating a log-depth DAG instead of linear.""" diff --git a/mirgecom/gas_model.py b/mirgecom/gas_model.py index 54a836181..b4c5cf799 100644 --- a/mirgecom/gas_model.py +++ b/mirgecom/gas_model.py @@ -10,6 +10,7 @@ .. autoclass:: FluidState .. autoclass:: ViscousFluidState +.. autoclass:: PorousFlowFluidState Fluid State Handling Utilities ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -406,8 +407,7 @@ def make_fluid_state(cv, gas_model, tortuosity=gas_model.wall_model.tortuosity(tau) ) - temperature = gas_model.get_temperature( - cv=cv, wv=wv, tseed=temperature_seed) + temperature = gas_model.get_temperature(cv=cv, wv=wv, tseed=temperature_seed) pressure = gas_model.get_pressure(cv, wv, temperature) @@ -425,20 +425,9 @@ def make_fluid_state(cv, gas_model, species_enthalpies=gas_model.eos.species_enthalpies(cv, temperature), ) - # ~~~ Modify transport vars to include solid effects - # TODO create a new transport class exclusive for porous media flow? - gas_tv = gas_model.transport.transport_vars(cv=cv, dv=dv, eos=gas_model.eos) - - tv = GasTransportVars( - bulk_viscosity=( - gas_tv.bulk_viscosity), - viscosity=gas_model.viscosity( - wv, temperature, gas_tv), - thermal_conductivity=gas_model.thermal_conductivity( - cv, wv, temperature, gas_tv), - species_diffusivity=gas_model.species_diffusivity( - wv, temperature, gas_tv), - ) + # FIXME I have to pass "gas_model" but this class is internal to "gas_model" + # Seems dumb to me but I dont know to access the other classes... + tv = gas_model.transport.transport_vars(cv, dv, wv, gas_model) return PorousFlowFluidState(cv=cv, dv=dv, tv=tv, wv=wv) diff --git a/mirgecom/materials/initializer.py b/mirgecom/materials/initializer.py index 8f744f29e..9184745dc 100644 --- a/mirgecom/materials/initializer.py +++ b/mirgecom/materials/initializer.py @@ -74,7 +74,7 @@ def __call__(self, x_vec, gas_model): gas_model: :class:`mirgecom.wall_model.PorousFlowModel` Equation of state class """ - actx = x_vec.array_context + actx = x_vec[0].array_context zeros = actx.np.zeros_like(x_vec[0]) pressure = self._pres + zeros diff --git a/mirgecom/transport.py b/mirgecom/transport.py index 4f6e5bf6f..20c0deaa7 100644 --- a/mirgecom/transport.py +++ b/mirgecom/transport.py @@ -16,6 +16,7 @@ .. autoclass:: MixtureAveragedTransport .. autoclass:: ArtificialViscosityTransportDiv .. autoclass:: ArtificialViscosityTransportDiv2 +.. autoclass:: PorousMediaTransport Exceptions ^^^^^^^^^^ @@ -50,7 +51,6 @@ from dataclasses import dataclass from arraycontext import dataclass_array_container import numpy as np -from meshmode.mesh import BTAG_ALL, BTAG_NONE # noqa from meshmode.dof_array import DOFArray from mirgecom.fluid import ConservedVars from mirgecom.eos import GasEOS, GasDependentVars @@ -698,3 +698,81 @@ def species_diffusivity(self, cv: ConservedVars, eos: Optional[GasEOS] = None) -> DOFArray: r"""Get the vector of species diffusivities, ${d}_{\alpha}$.""" return self._physical_transport.species_diffusivity(cv, dv, eos) + + +class PorousWallTransport(TransportModel): + r"""Transport model for add artificial viscosity. + + Inherits from (and implements) :class:`TransportModel`. + + Takes a physical transport model and adds the artificial viscosity + contribution to it. Defaults to simple transport with inviscid settings. + This is equivalent to inviscid flow with artifical viscosity enabled. + + .. automethod:: __init__ + .. automethod:: bulk_viscosity + .. automethod:: viscosity + .. automethod:: volume_viscosity + .. automethod:: thermal_conductivity + .. automethod:: species_diffusivity + """ + + from mirgecom.wall_model import PorousWallVars, PorousFlowModel + + def __init__(self, base_transport): + """Initialize uniform, constant transport properties.""" + self.base_transport = base_transport + + def bulk_viscosity(self, cv, dv, wv, flow_model) -> DOFArray: + r"""Get the bulk viscosity for the gas, $\mu_{B}$.""" + return cv.mass*0.0 + + def volume_viscosity(self, cv: ConservedVars, dv: GasDependentVars, + wv: PorousWallVars, flow_model: PorousFlowModel) -> DOFArray: + r"""Get the 2nd viscosity coefficent, $\lambda$.""" + return (self.bulk_viscosity(cv, dv, wv, flow_model) + - 2./3. * self.viscosity(cv, dv, wv, flow_model)) + + def viscosity(self, cv: ConservedVars, dv: GasDependentVars, + wv: PorousWallVars, flow_model: PorousFlowModel) -> DOFArray: + """Viscosity of the gas through the (porous) wall.""" + return 1.0/wv.void_fraction*( + self.base_transport.viscosity(cv, dv, flow_model.eos)) + + def thermal_conductivity(self, cv: ConservedVars, dv: GasDependentVars, + wv: PorousWallVars, flow_model: PorousFlowModel) -> DOFArray: + r"""Return the effective thermal conductivity of the gas+solid. + + It is a function of temperature and degradation progress. As the + fibers are oxidized, they reduce their cross area and, consequently, + their ability to conduct heat. + + It is evaluated using a mass-weighted average given by + + .. math:: + \frac{\rho_s \kappa_s + \rho_g \kappa_g}{\rho_s + \rho_g} + """ + y_g = cv.mass/(cv.mass + wv.density) + y_s = 1.0 - y_g + kappa_s = flow_model.wall_model.thermal_conductivity(dv.temperature, wv.tau) + kappa_g = self.base_transport.thermal_conductivity(cv, dv, flow_model.eos) + + return y_s*kappa_s + y_g*kappa_g + + def species_diffusivity(self, cv: ConservedVars, dv: GasDependentVars, + wv: PorousWallVars, flow_model: PorousFlowModel) -> DOFArray: + """Mass diffusivity of gaseous species through the (porous) wall.""" + return 1.0/wv.tortuosity*( + self.base_transport.species_diffusivity(cv, dv, flow_model.eos)) + + # FIXME I have to pass "flow_model" but this class is internal to "flow_model".. + # Seems dumb to me but I dont know to access the other classes... + def transport_vars(self, cv: ConservedVars, dv: GasDependentVars, + wv: PorousWallVars, flow_model: PorousFlowModel) -> GasTransportVars: + r"""Compute the transport properties from the conserved state.""" + return GasTransportVars( + bulk_viscosity=self.bulk_viscosity(cv, dv, wv, flow_model), + viscosity=self.viscosity(cv, dv, wv, flow_model), + thermal_conductivity=self.thermal_conductivity(cv, dv, wv, flow_model), + species_diffusivity=self.species_diffusivity(cv, dv, wv, flow_model) + ) diff --git a/mirgecom/wall_model.py b/mirgecom/wall_model.py index b08367d12..98b9ed19c 100644 --- a/mirgecom/wall_model.py +++ b/mirgecom/wall_model.py @@ -44,10 +44,9 @@ with_container_arithmetic, get_container_context_recursively ) -from mirgecom.transport import TransportModel from mirgecom.fluid import ConservedVars from mirgecom.eos import GasEOS -from mirgecom.transport import GasTransportVars +from mirgecom.transport import TransportModel @with_container_arithmetic(bcast_obj_array=False, @@ -270,26 +269,6 @@ def heat_capacity(self, cv: ConservedVars, wv: PorousWallVars, """Return the heat capacity of the gas+solid material.""" raise NotImplementedError() - @abstractmethod - def viscosity(self, wv: PorousWallVars, temperature: DOFArray, - gas_tv: GasTransportVars) -> DOFArray: - """Viscosity of the gas through the (porous) wall.""" - raise NotImplementedError() - - @abstractmethod - def thermal_conductivity(self, cv: ConservedVars, - wv: PorousWallVars, - temperature: DOFArray, - gas_tv: GasTransportVars) -> DOFArray: - r"""Return the effective thermal conductivity of the gas+solid.""" - raise NotImplementedError() - - @abstractmethod - def species_diffusivity(self, wv: PorousWallVars, - temperature: DOFArray, gas_tv: GasTransportVars) -> DOFArray: - """Mass diffusivity of gaseous species through the (porous) wall.""" - raise NotImplementedError() - @dataclass(frozen=True) class PorousFlowModel(PorousFlowEOS): @@ -305,9 +284,6 @@ class PorousFlowModel(PorousFlowEOS): .. automethod:: get_pressure .. automethod:: internal_energy .. automethod:: heat_capacity - .. automethod:: viscosity - .. automethod:: thermal_conductivity - .. automethod:: species_diffusivity .. automethod:: decomposition_progress """ @@ -385,38 +361,3 @@ def heat_capacity(self, cv: ConservedVars, wv: PorousWallVars, """Return the heat capacity of the gas+solid material.""" return (cv.mass*self.eos.heat_capacity_cv(cv, temperature) + wv.density*self.wall_model.heat_capacity(temperature, wv.tau)) - - # TODO: maybe create a WallTransportVars? - def viscosity(self, wv: PorousWallVars, temperature: DOFArray, - gas_tv: GasTransportVars) -> DOFArray: - """Viscosity of the gas through the (porous) wall.""" - return gas_tv.viscosity/wv.void_fraction - - # TODO: maybe create a WallTransportVars? - def thermal_conductivity(self, cv: ConservedVars, - wv: PorousWallVars, - temperature: DOFArray, - gas_tv: GasTransportVars) -> DOFArray: - r"""Return the effective thermal conductivity of the gas+solid. - - It is a function of temperature and degradation progress. As the - fibers are oxidized, they reduce their cross area and, consequently, - their ability to conduct heat. - - It is evaluated using a mass-weighted average given by - - .. math:: - \frac{\rho_s \kappa_s + \rho_g \kappa_g}{\rho_s + \rho_g} - """ - y_g = cv.mass/(cv.mass + wv.density) - y_s = 1.0 - y_g - kappa_s = self.wall_model.thermal_conductivity(temperature, wv.tau) - kappa_g = gas_tv.thermal_conductivity - - return y_s*kappa_s + y_g*kappa_g - - # TODO: maybe create a WallTransportVars? - def species_diffusivity(self, wv: PorousWallVars, - temperature: DOFArray, gas_tv: GasTransportVars) -> DOFArray: - """Mass diffusivity of gaseous species through the (porous) wall.""" - return gas_tv.species_diffusivity/wv.tortuosity From 97815985390edaa9e5f3fbc15fa3f2bca83dd4c6 Mon Sep 17 00:00:00 2001 From: Tulio Date: Sat, 22 Jul 2023 18:17:16 -0500 Subject: [PATCH 17/45] mypy --- mirgecom/transport.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/mirgecom/transport.py b/mirgecom/transport.py index 20c0deaa7..ced64b6da 100644 --- a/mirgecom/transport.py +++ b/mirgecom/transport.py @@ -723,24 +723,29 @@ def __init__(self, base_transport): """Initialize uniform, constant transport properties.""" self.base_transport = base_transport - def bulk_viscosity(self, cv, dv, wv, flow_model) -> DOFArray: + def bulk_viscosity(self, cv: ConservedVars, # type: ignore[override] + dv: GasDependentVars, wv: PorousWallVars, + flow_model: PorousFlowModel) -> DOFArray: r"""Get the bulk viscosity for the gas, $\mu_{B}$.""" - return cv.mass*0.0 + return self.base_transport.bulk_viscosity(cv, dv, flow_model.eos) - def volume_viscosity(self, cv: ConservedVars, dv: GasDependentVars, - wv: PorousWallVars, flow_model: PorousFlowModel) -> DOFArray: + def volume_viscosity(self, cv: ConservedVars, # type: ignore[override] + dv: GasDependentVars, wv: PorousWallVars, + flow_model: PorousFlowModel) -> DOFArray: r"""Get the 2nd viscosity coefficent, $\lambda$.""" return (self.bulk_viscosity(cv, dv, wv, flow_model) - 2./3. * self.viscosity(cv, dv, wv, flow_model)) - def viscosity(self, cv: ConservedVars, dv: GasDependentVars, - wv: PorousWallVars, flow_model: PorousFlowModel) -> DOFArray: + def viscosity(self, cv: ConservedVars, # type: ignore[override] + dv: GasDependentVars, wv: PorousWallVars, + flow_model: PorousFlowModel) -> DOFArray: """Viscosity of the gas through the (porous) wall.""" return 1.0/wv.void_fraction*( self.base_transport.viscosity(cv, dv, flow_model.eos)) - def thermal_conductivity(self, cv: ConservedVars, dv: GasDependentVars, - wv: PorousWallVars, flow_model: PorousFlowModel) -> DOFArray: + def thermal_conductivity(self, cv: ConservedVars, # type: ignore[override] + dv: GasDependentVars, wv: PorousWallVars, + flow_model: PorousFlowModel) -> DOFArray: r"""Return the effective thermal conductivity of the gas+solid. It is a function of temperature and degradation progress. As the @@ -759,16 +764,18 @@ def thermal_conductivity(self, cv: ConservedVars, dv: GasDependentVars, return y_s*kappa_s + y_g*kappa_g - def species_diffusivity(self, cv: ConservedVars, dv: GasDependentVars, - wv: PorousWallVars, flow_model: PorousFlowModel) -> DOFArray: + def species_diffusivity(self, cv: ConservedVars, # type: ignore[override] + dv: GasDependentVars, wv: PorousWallVars, + flow_model: PorousFlowModel) -> DOFArray: """Mass diffusivity of gaseous species through the (porous) wall.""" return 1.0/wv.tortuosity*( self.base_transport.species_diffusivity(cv, dv, flow_model.eos)) # FIXME I have to pass "flow_model" but this class is internal to "flow_model".. # Seems dumb to me but I dont know to access the other classes... - def transport_vars(self, cv: ConservedVars, dv: GasDependentVars, - wv: PorousWallVars, flow_model: PorousFlowModel) -> GasTransportVars: + def transport_vars(self, cv: ConservedVars, # type: ignore[override] + dv: GasDependentVars, wv: PorousWallVars, + flow_model: PorousFlowModel) -> GasTransportVars: r"""Compute the transport properties from the conserved state.""" return GasTransportVars( bulk_viscosity=self.bulk_viscosity(cv, dv, wv, flow_model), From a39e413831018abbd8a9c8fa73d6e73165127f2e Mon Sep 17 00:00:00 2001 From: Tulio Date: Sat, 22 Jul 2023 18:37:40 -0500 Subject: [PATCH 18/45] Fix SolidWallInitializer --- mirgecom/materials/initializer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mirgecom/materials/initializer.py b/mirgecom/materials/initializer.py index 9184745dc..46350425b 100644 --- a/mirgecom/materials/initializer.py +++ b/mirgecom/materials/initializer.py @@ -49,7 +49,8 @@ def __call__(self, x_vec, wall_model): wall_model: :class:`mirgecom.wall_model.SolidWallModel` Equation of state class """ - mass = wall_model.density() + actx = x_vec[0].array_context + mass = wall_model.density() + actx.np.zeros_like(x_vec[0]) energy = mass * wall_model.enthalpy(self._temp) return SolidWallConservedVars(mass=mass, energy=energy) From 9c3110bba6d9ff88044fcd80c5c47392b1a6bdf9 Mon Sep 17 00:00:00 2001 From: Tulio Date: Sat, 22 Jul 2023 19:05:50 -0500 Subject: [PATCH 19/45] Improve docs --- doc/operators/composite_materials.rst | 10 +++- doc/operators/thermally_coupled.rst | 13 +++++ mirgecom/transport.py | 2 +- mirgecom/wall_model.py | 70 +++++++++++++-------------- 4 files changed, 56 insertions(+), 39 deletions(-) diff --git a/doc/operators/composite_materials.rst b/doc/operators/composite_materials.rst index 3fd8dbda7..0906dd48f 100644 --- a/doc/operators/composite_materials.rst +++ b/doc/operators/composite_materials.rst @@ -1,13 +1,21 @@ Wall Degradation Modeling ========================= -.. automodule:: mirgecom.wall_model +Porous Media EOS +^^^^^^^^^^^^^^^^ +.. autoclass:: mirgecom.wall_model.PorousFlowModel + +Wall variables +^^^^^^^^^^^^^^ +.. autoclass:: mirgecom.wall_model.PorousWallVars Model-specific properties ^^^^^^^^^^^^^^^^^^^^^^^^^ The properties of the materials are defined in specific files and used by :class:`~mirgecom.wall_model.PorousWallProperties`. +.. autoclass:: mirgecom.wall_model.PorousWallProperties + Carbon fiber ------------ .. automodule:: mirgecom.materials.carbon_fiber diff --git a/doc/operators/thermally_coupled.rst b/doc/operators/thermally_coupled.rst index 3f47dc5f1..c710f94d5 100644 --- a/doc/operators/thermally_coupled.rst +++ b/doc/operators/thermally_coupled.rst @@ -2,3 +2,16 @@ Thermally Coupled Fluid-Wall ============================ .. automodule:: mirgecom.multiphysics.thermally_coupled_fluid_wall + +Wall variables +^^^^^^^^^^^^^^ +.. autoclass:: mirgecom.wall_model.SolidWallConservedVars +.. autoclass:: mirgecom.wall_model.SolidWallDependentVars + +Helper class +------------ +.. autoclass:: mirgecom.wall_model.SolidWallState + +Model-specific properties +^^^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: mirgecom.wall_model.SolidWallModel diff --git a/mirgecom/transport.py b/mirgecom/transport.py index ced64b6da..16f6c5b0c 100644 --- a/mirgecom/transport.py +++ b/mirgecom/transport.py @@ -16,7 +16,7 @@ .. autoclass:: MixtureAveragedTransport .. autoclass:: ArtificialViscosityTransportDiv .. autoclass:: ArtificialViscosityTransportDiv2 -.. autoclass:: PorousMediaTransport +.. autoclass:: PorousWallTransport Exceptions ^^^^^^^^^^ diff --git a/mirgecom/wall_model.py b/mirgecom/wall_model.py index 98b9ed19c..9c9d55169 100644 --- a/mirgecom/wall_model.py +++ b/mirgecom/wall_model.py @@ -1,13 +1,4 @@ -""":mod:`mirgecom.wall_model` handles the EOS for wall model. - -.. autoclass:: SolidWallConservedVars -.. autoclass:: SolidWallDependentVars -.. autoclass:: SolidWallState -.. autoclass:: SolidWallModel -.. autoclass:: PorousWallVars -.. autoclass:: PorousWallProperties -.. autoclass:: PorousFlowModel -""" +""":mod:`mirgecom.wall_model` handles the EOS for wall model.""" __copyright__ = """ Copyright (C) 2023 University of Illinois Board of Trustees @@ -89,6 +80,11 @@ class SolidWallState: class SolidWallModel: """Model for calculating wall quantities for heat conduction only materials. + Since different materials can be coupled together in the same domain + governed by diffusion equation, this wall model works as a wrapper of + driver-defined functions that prescribe the actual properties and their + spatial distribution. + .. automethod:: density .. automethod:: heat_capacity .. automethod:: enthalpy @@ -127,11 +123,11 @@ def thermal_conductivity(self, temperature): """Return the wall thermal conductivity for all components.""" return self._thermal_conductivity_func(temperature) - def get_temperature(self, wv, tseed=None): + def get_temperature(self, wv, tseed=None, niter=3): """Evaluate the temperature based on the energy.""" if tseed is not None: temp = tseed*1.0 - for _ in range(0, 3): + for _ in range(0, niter): h = self.enthalpy(temp) cp = self.heat_capacity(temp) temp = temp - (h - wv.energy/wv.mass)/cp @@ -139,15 +135,38 @@ def get_temperature(self, wv, tseed=None): return wv.energy/(self.density()*self.heat_capacity()) - def dependent_vars(self, wv, tseed=None): + def dependent_vars(self, wv, tseed=None, niter=3): """Return solid wall dependent variables.""" - temperature = self.get_temperature(wv, tseed) + temperature = self.get_temperature(wv, tseed, niter) kappa = self.thermal_conductivity(temperature) return SolidWallDependentVars( thermal_conductivity=kappa, temperature=temperature) +@dataclass_array_container +@dataclass(frozen=True, eq=False) +class PorousWallVars: + """Variables for the (porous) fluid state. + + .. attribute:: material_densities + .. attribute:: tau + .. attribute:: void_fraction + .. attribute:: emissivity + .. attribute:: permeability + .. attribute:: tortuosity + .. attribute:: density + """ + + material_densities: Union[DOFArray, np.ndarray] + tau: DOFArray + void_fraction: DOFArray + emissivity: DOFArray + permeability: DOFArray + tortuosity: DOFArray + density: DOFArray + + class PorousWallProperties: """Abstract interface for porous media domains. @@ -209,29 +228,6 @@ def decomposition_progress(self, mass: DOFArray) -> DOFArray: raise NotImplementedError() -@dataclass_array_container -@dataclass(frozen=True, eq=False) -class PorousWallVars: - """Variables for the (porous) fluid state. - - .. attribute:: material_densities - .. attribute:: tau - .. attribute:: void_fraction - .. attribute:: emissivity - .. attribute:: permeability - .. attribute:: tortuosity - .. attribute:: density - """ - - material_densities: Union[DOFArray, np.ndarray] - tau: DOFArray - void_fraction: DOFArray - emissivity: DOFArray - permeability: DOFArray - tortuosity: DOFArray - density: DOFArray - - class PorousFlowEOS: """Abstract interface to equation of porous flow class.""" From d5cf0966fdd6b0727a3b897729ea2de49a17face Mon Sep 17 00:00:00 2001 From: Tulio Date: Sat, 22 Jul 2023 19:12:35 -0500 Subject: [PATCH 20/45] Improve again the docs --- mirgecom/wall_model.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/mirgecom/wall_model.py b/mirgecom/wall_model.py index 9c9d55169..1c19b38fb 100644 --- a/mirgecom/wall_model.py +++ b/mirgecom/wall_model.py @@ -91,6 +91,7 @@ class SolidWallModel: .. automethod:: thermal_diffusivity .. automethod:: thermal_conductivity .. automethod:: get_temperature + .. automethod:: dependent_vars """ def __init__(self, density_func, enthalpy_func, heat_capacity_func, @@ -100,30 +101,31 @@ def __init__(self, density_func, enthalpy_func, heat_capacity_func, self._heat_capacity_func = heat_capacity_func self._thermal_conductivity_func = thermal_conductivity_func - def density(self): + def density(self) -> DOFArray: """Return the wall density for all components.""" return self._density_func() - def heat_capacity(self, temperature=None): + def heat_capacity(self, temperature: DOFArray = None) -> DOFArray: """Return the wall heat_capacity for all components.""" return self._heat_capacity_func(temperature) - def enthalpy(self, temperature): + def enthalpy(self, temperature: DOFArray) -> DOFArray: """Return the wall enthalpy for all components.""" return self._enthalpy_func(temperature) - def thermal_diffusivity(self, mass, temperature, - thermal_conductivity=None): + def thermal_diffusivity(self, mass: DOFArray, temperature: DOFArray, + thermal_conductivity: DOFArray = None) -> DOFArray: """Return the wall thermal diffusivity for all components.""" if thermal_conductivity is None: thermal_conductivity = self.thermal_conductivity(temperature) return thermal_conductivity/(mass * self.heat_capacity(temperature)) - def thermal_conductivity(self, temperature): + def thermal_conductivity(self, temperature: DOFArray) -> DOFArray: """Return the wall thermal conductivity for all components.""" return self._thermal_conductivity_func(temperature) - def get_temperature(self, wv, tseed=None, niter=3): + def get_temperature(self, wv: SolidWallConservedVars, + tseed: DOFArray = None, niter: int = 3) -> DOFArray: """Evaluate the temperature based on the energy.""" if tseed is not None: temp = tseed*1.0 @@ -135,7 +137,8 @@ def get_temperature(self, wv, tseed=None, niter=3): return wv.energy/(self.density()*self.heat_capacity()) - def dependent_vars(self, wv, tseed=None, niter=3): + def dependent_vars(self, wv: SolidWallConservedVars, + tseed: DOFArray = None, niter: int = 3) -> SolidWallDependentVars: """Return solid wall dependent variables.""" temperature = self.get_temperature(wv, tseed, niter) kappa = self.thermal_conductivity(temperature) From d5a62f34c219a3612aadea092ad656357d5f514e Mon Sep 17 00:00:00 2001 From: Tulio Date: Sat, 22 Jul 2023 19:14:18 -0500 Subject: [PATCH 21/45] Do NOT change thermally_coupled.rst --- doc/operators/thermally_coupled.rst | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/doc/operators/thermally_coupled.rst b/doc/operators/thermally_coupled.rst index c710f94d5..3f47dc5f1 100644 --- a/doc/operators/thermally_coupled.rst +++ b/doc/operators/thermally_coupled.rst @@ -2,16 +2,3 @@ Thermally Coupled Fluid-Wall ============================ .. automodule:: mirgecom.multiphysics.thermally_coupled_fluid_wall - -Wall variables -^^^^^^^^^^^^^^ -.. autoclass:: mirgecom.wall_model.SolidWallConservedVars -.. autoclass:: mirgecom.wall_model.SolidWallDependentVars - -Helper class ------------- -.. autoclass:: mirgecom.wall_model.SolidWallState - -Model-specific properties -^^^^^^^^^^^^^^^^^^^^^^^^^ -.. autoclass:: mirgecom.wall_model.SolidWallModel From a398d9ea01097afe891d6d0db14a038a738cefcc Mon Sep 17 00:00:00 2001 From: Tulio Date: Mon, 24 Jul 2023 16:11:46 -0500 Subject: [PATCH 22/45] Fix transport doc --- mirgecom/transport.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/mirgecom/transport.py b/mirgecom/transport.py index 16f6c5b0c..459a23ea9 100644 --- a/mirgecom/transport.py +++ b/mirgecom/transport.py @@ -701,13 +701,12 @@ def species_diffusivity(self, cv: ConservedVars, class PorousWallTransport(TransportModel): - r"""Transport model for add artificial viscosity. + r"""Transport model for porous media flow. Inherits from (and implements) :class:`TransportModel`. - Takes a physical transport model and adds the artificial viscosity - contribution to it. Defaults to simple transport with inviscid settings. - This is equivalent to inviscid flow with artifical viscosity enabled. + Takes a any transport model and modify it to consider the interaction + with the porous materials. .. automethod:: __init__ .. automethod:: bulk_viscosity @@ -720,7 +719,7 @@ class PorousWallTransport(TransportModel): from mirgecom.wall_model import PorousWallVars, PorousFlowModel def __init__(self, base_transport): - """Initialize uniform, constant transport properties.""" + """Initialize transport model.""" self.base_transport = base_transport def bulk_viscosity(self, cv: ConservedVars, # type: ignore[override] @@ -739,7 +738,7 @@ def volume_viscosity(self, cv: ConservedVars, # type: ignore[override] def viscosity(self, cv: ConservedVars, # type: ignore[override] dv: GasDependentVars, wv: PorousWallVars, flow_model: PorousFlowModel) -> DOFArray: - """Viscosity of the gas through the (porous) wall.""" + """Viscosity of the gas through the porous wall.""" return 1.0/wv.void_fraction*( self.base_transport.viscosity(cv, dv, flow_model.eos)) @@ -767,7 +766,7 @@ def thermal_conductivity(self, cv: ConservedVars, # type: ignore[override] def species_diffusivity(self, cv: ConservedVars, # type: ignore[override] dv: GasDependentVars, wv: PorousWallVars, flow_model: PorousFlowModel) -> DOFArray: - """Mass diffusivity of gaseous species through the (porous) wall.""" + """Mass diffusivity of gaseous species through the porous wall.""" return 1.0/wv.tortuosity*( self.base_transport.species_diffusivity(cv, dv, flow_model.eos)) From 2fdc22da66361569360581b9e46aec1624067339 Mon Sep 17 00:00:00 2001 From: Tulio Date: Mon, 24 Jul 2023 17:20:26 -0500 Subject: [PATCH 23/45] Improve docs --- doc/index.rst | 2 +- .../composite_material.rst} | 31 ++---------- doc/{ => mathematical}/fluid.rst | 5 +- doc/mathematical/mathematical.rst | 8 ++++ doc/operators/operators.rst | 1 - doc/operators/thermally_coupled.rst | 47 +++++++++++++++++++ 6 files changed, 62 insertions(+), 32 deletions(-) rename doc/{operators/composite_materials.rst => mathematical/composite_material.rst} (91%) rename doc/{ => mathematical}/fluid.rst (98%) create mode 100644 doc/mathematical/mathematical.rst diff --git a/doc/index.rst b/doc/index.rst index 0eb49db5f..26f606155 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -70,7 +70,7 @@ Table of Contents .. toctree:: :numbered: - fluid + mathematical/mathematical discretization operators/operators support/support diff --git a/doc/operators/composite_materials.rst b/doc/mathematical/composite_material.rst similarity index 91% rename from doc/operators/composite_materials.rst rename to doc/mathematical/composite_material.rst index 0906dd48f..f84d0bd4e 100644 --- a/doc/operators/composite_materials.rst +++ b/doc/mathematical/composite_material.rst @@ -1,31 +1,8 @@ -Wall Degradation Modeling -========================= - -Porous Media EOS -^^^^^^^^^^^^^^^^ -.. autoclass:: mirgecom.wall_model.PorousFlowModel - -Wall variables -^^^^^^^^^^^^^^ -.. autoclass:: mirgecom.wall_model.PorousWallVars - -Model-specific properties -^^^^^^^^^^^^^^^^^^^^^^^^^ - The properties of the materials are defined in specific files and used by - :class:`~mirgecom.wall_model.PorousWallProperties`. - -.. autoclass:: mirgecom.wall_model.PorousWallProperties - -Carbon fiber ------------- -.. automodule:: mirgecom.materials.carbon_fiber - -TACOT ------ -.. automodule:: mirgecom.materials.tacot +Wall Degradation +================ Carbon Fiber Oxidation -^^^^^^^^^^^^^^^^^^^^^^ +---------------------- This section covers the response of carbon fiber when exposed to oxygen. The carbon fibers are characterized as a highly porous material, @@ -93,7 +70,7 @@ Carbon Fiber Oxidation Note that :mod:`pyrometheus` is used to handle the species properties. Composite Materials -^^^^^^^^^^^^^^^^^^^ +------------------- This section covers the response of composite materials made of phenolic resin and carbon fibers. diff --git a/doc/fluid.rst b/doc/mathematical/fluid.rst similarity index 98% rename from doc/fluid.rst rename to doc/mathematical/fluid.rst index 91263d68b..5ad1bdc3f 100644 --- a/doc/fluid.rst +++ b/doc/mathematical/fluid.rst @@ -1,6 +1,5 @@ -================================ -Mathematical Model of Fluid Flow -================================ +Fluid Flow +========== .. raw:: latex diff --git a/doc/mathematical/mathematical.rst b/doc/mathematical/mathematical.rst new file mode 100644 index 000000000..d0fe57fb2 --- /dev/null +++ b/doc/mathematical/mathematical.rst @@ -0,0 +1,8 @@ +================== +Mathematical Model +================== + +.. toctree:: + + fluid + composite_material diff --git a/doc/operators/operators.rst b/doc/operators/operators.rst index f649e240e..a445df09e 100644 --- a/doc/operators/operators.rst +++ b/doc/operators/operators.rst @@ -9,4 +9,3 @@ Operators artificial_viscosity gas-dynamics thermally_coupled - composite_materials diff --git a/doc/operators/thermally_coupled.rst b/doc/operators/thermally_coupled.rst index 3f47dc5f1..cd747b056 100644 --- a/doc/operators/thermally_coupled.rst +++ b/doc/operators/thermally_coupled.rst @@ -2,3 +2,50 @@ Thermally Coupled Fluid-Wall ============================ .. automodule:: mirgecom.multiphysics.thermally_coupled_fluid_wall + +Solid materials +^^^^^^^^^^^^^^^ + +Wall variables +-------------- +.. autoclass:: mirgecom.wall_model.SolidWallConservedVars +.. autoclass:: mirgecom.wall_model.SolidWallDependentVars +.. autoclass:: mirgecom.wall_model.SolidWallState + +EOS +--- +.. autoclass:: mirgecom.wall_model.SolidWallModel + +Model-specific properties +------------------------- + The properties are defined exclusively at the driver. + +Porous materials +^^^^^^^^^^^^^^^^ + +Wall variables +-------------- +.. autoclass:: mirgecom.wall_model.PorousWallVars + +Porous Media EOS +---------------- +.. autoclass:: mirgecom.wall_model.PorousFlowModel + +Model-specific properties +------------------------- + The properties of the materials are defined in specific files and used by + :class:`~mirgecom.wall_model.PorousWallProperties`. + +.. autoclass:: mirgecom.wall_model.PorousWallProperties + +Carbon fiber +"""""""""""" +.. automodule:: mirgecom.materials.carbon_fiber + +TACOT +""""" +.. automodule:: mirgecom.materials.tacot + +Helper Functions +^^^^^^^^^^^^^^^^ +.. automodule:: mirgecom.materials.initializer From 01c205a032c72b75b0fc935d191292ea7833fc89 Mon Sep 17 00:00:00 2001 From: Tulio Date: Tue, 25 Jul 2023 10:02:18 -0500 Subject: [PATCH 24/45] Finish docs (?) and include function for user-prescribed materials --- mirgecom/gas_model.py | 4 +- mirgecom/materials/__init__.py | 1 + mirgecom/materials/initializer.py | 23 ++++-- mirgecom/materials/simple_material.py | 109 ++++++++++++++++++++++++++ mirgecom/wall_model.py | 61 ++++++++++---- 5 files changed, 175 insertions(+), 23 deletions(-) create mode 100644 mirgecom/materials/simple_material.py diff --git a/mirgecom/gas_model.py b/mirgecom/gas_model.py index b4c5cf799..8fef434a3 100644 --- a/mirgecom/gas_model.py +++ b/mirgecom/gas_model.py @@ -407,7 +407,9 @@ def make_fluid_state(cv, gas_model, tortuosity=gas_model.wall_model.tortuosity(tau) ) - temperature = gas_model.get_temperature(cv=cv, wv=wv, tseed=temperature_seed) + # FIXME is there a way to pass the argument to "niter"? + temperature = gas_model.get_temperature(cv=cv, wv=wv, + tseed=temperature_seed, niter=3) pressure = gas_model.get_pressure(cv, wv, temperature) diff --git a/mirgecom/materials/__init__.py b/mirgecom/materials/__init__.py index 59f0a1dd7..db33e0df8 100644 --- a/mirgecom/materials/__init__.py +++ b/mirgecom/materials/__init__.py @@ -28,4 +28,5 @@ .. automodule:: mirgecom.materials.initializer .. automodule:: mirgecom.materials.carbon_fiber .. automodule:: mirgecom.materials.tacot +.. automodule:: mirgecom.materials.simple_material """ diff --git a/mirgecom/materials/initializer.py b/mirgecom/materials/initializer.py index 46350425b..e9dac2f88 100644 --- a/mirgecom/materials/initializer.py +++ b/mirgecom/materials/initializer.py @@ -58,14 +58,16 @@ def __call__(self, x_vec, wall_model): class PorousWallInitializer: """Initializer for porous materials.""" - def __init__(self, pressure, temperature, species, material_densities): + def __init__(self, temperature, species, material_densities, + pressure=None, density=None): self._pres = pressure + self._mass = density self._y = species self._temp = temperature self._wall_density = material_densities - def __call__(self, x_vec, gas_model): + def __call__(self, dim, x_vec, gas_model): """Evaluate the wall+gas properties for porous materials. Parameters @@ -78,18 +80,23 @@ def __call__(self, x_vec, gas_model): actx = x_vec[0].array_context zeros = actx.np.zeros_like(x_vec[0]) - pressure = self._pres + zeros temperature = self._temp + zeros species_mass_frac = self._y + zeros + wall_density = self._wall_density + zeros - tau = gas_model.decomposition_progress(self._wall_density) + tau = gas_model.decomposition_progress(wall_density) eps_gas = gas_model.wall_model.void_fraction(tau) - eps_rho_gas = eps_gas*gas_model.eos.get_density(pressure, temperature, - species_mass_frac) + if self._mass is None: + pressure = self._pres + zeros + eps_rho_gas = eps_gas*gas_model.eos.get_density(pressure, + temperature, species_mass_frac) + else: + density = self._mass + zeros + eps_rho_gas = eps_gas*density # internal energy (kinetic energy is neglected) - eps_rho_solid = sum(self._wall_density) + eps_rho_solid = sum(wall_density) bulk_energy = ( eps_rho_solid*gas_model.wall_model.enthalpy(temperature, tau) + eps_rho_gas*gas_model.eos.get_internal_energy(temperature, @@ -100,5 +107,5 @@ def __call__(self, x_vec, gas_model): species_mass = eps_rho_gas*species_mass_frac - return make_conserved(dim=2, mass=eps_rho_gas, energy=bulk_energy, + return make_conserved(dim=dim, mass=eps_rho_gas, energy=bulk_energy, momentum=momentum, species_mass=species_mass) diff --git a/mirgecom/materials/simple_material.py b/mirgecom/materials/simple_material.py new file mode 100644 index 000000000..13fbbda75 --- /dev/null +++ b/mirgecom/materials/simple_material.py @@ -0,0 +1,109 @@ +""":mod:`mirgecom.materials.simple_material` for user-defined materials.""" + +__copyright__ = """ +Copyright (C) 2023 University of Illinois Board of Trustees +""" + +__license__ = """ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +from typing import Optional +from meshmode.dof_array import DOFArray +from mirgecom.wall_model import PorousWallProperties + + +class SolidProperties(PorousWallProperties): + """Evaluate the properties of the solid state containing only fibers. + + .. automethod:: void_fraction + .. automethod:: enthalpy + .. automethod:: heat_capacity + .. automethod:: thermal_conductivity + .. automethod:: volume_fraction + .. automethod:: permeability + .. automethod:: emissivity + .. automethod:: tortuosity + .. automethod:: decomposition_progress + """ + + def __init__(self, char_mass, virgin_mass, enthalpy_func, heat_capacity_func, + thermal_conductivity_func, volume_fraction_func, permeability_func, + emissivity_func, tortuosity_func): + + self._char_mass = char_mass + self._virgin_mass = virgin_mass + self._enthalpy_func = enthalpy_func + self._heat_capacity_func = heat_capacity_func + self._thermal_conductivity_func = thermal_conductivity_func + self._volume_fraction_func = volume_fraction_func + self._permeability_func = permeability_func + self._emissivity_func = emissivity_func + self._tortuosity_func = tortuosity_func + + def void_fraction(self, tau: DOFArray) -> DOFArray: + r"""Return the volumetric fraction $\epsilon$ filled with gas. + + The fractions of gas and solid phases must sum to one, + $\epsilon_g + \epsilon_s = 1$. Both depend only on the oxidation + progress ratio $\tau$. + """ + return 1.0 - self.volume_fraction(tau) + + def enthalpy(self, temperature: DOFArray, tau: Optional[DOFArray]) -> DOFArray: + r"""Evaluate the solid enthalpy $h_s$ of the fibers.""" + return self._enthalpy_func(temperature) + + def heat_capacity(self, temperature: DOFArray, + tau: Optional[DOFArray]) -> DOFArray: + r"""Evaluate the heat capacity $C_{p_s}$ of the fibers.""" + return self._heat_capacity_func(temperature) + + def thermal_conductivity(self, temperature: DOFArray, + tau: DOFArray) -> DOFArray: + r"""Evaluate the thermal conductivity $\kappa$ of the fibers. + + It employs a rescaling of the experimental data based on the fiber + shrinkage during the oxidation. + """ + return self._thermal_conductivity_func(temperature) + + def volume_fraction(self, tau: DOFArray) -> DOFArray: + r"""Fraction $\phi$ occupied by the solid.""" + actx = tau.array_context + return self._volume_fraction_func(tau) + actx.np.zeros_like(tau) + + def permeability(self, tau: DOFArray) -> DOFArray: + r"""Permeability $K$ of the porous material.""" + actx = tau.array_context + return self._permeability_func(tau) + actx.np.zeros_like(tau) + + def emissivity(self, tau: DOFArray) -> DOFArray: + """Emissivity for energy radiation.""" + actx = tau.array_context + return self._emissivity_func(tau) + actx.np.zeros_like(tau) + + def tortuosity(self, tau: DOFArray) -> DOFArray: + r"""Tortuosity $\eta$ affects the species diffusivity.""" + actx = tau.array_context + return self._tortuosity_func(tau) + actx.np.zeros_like(tau) + + def decomposition_progress(self, mass: DOFArray) -> DOFArray: + r"""Evaluate the progress rate $\tau$.""" + return 1.0 - (self._virgin_mass - mass)/self._virgin_mass diff --git a/mirgecom/wall_model.py b/mirgecom/wall_model.py index 1c19b38fb..1a3ee3626 100644 --- a/mirgecom/wall_model.py +++ b/mirgecom/wall_model.py @@ -272,33 +272,39 @@ def heat_capacity(self, cv: ConservedVars, wv: PorousWallVars, @dataclass(frozen=True) class PorousFlowModel(PorousFlowEOS): """ - ... + This is the main class of the porous media flow and is the + equivalent to :class:`~mirgecom.gas_model.GasModel`. It wraps: .. attribute:: wall_model + + The thermophysical properties of the wall material and its EOS. + .. attribute:: eos + + The thermophysical properties of the gas and its EOS. For now, only + mixtures are considered. + .. attribute:: transport + Transport class that governs how the gas flows through the porous + media. This is accounted for in + :class:`~mirgecom.transport.PorousWallTransport` + + It also include functions that combine the properties of the porous + material and the gas that permeates, yielding the actual porous flow EOS: + .. automethod:: solid_density + .. automethod:: decomposition_progress .. automethod:: get_temperature .. automethod:: get_pressure .. automethod:: internal_energy .. automethod:: heat_capacity - .. automethod:: decomposition_progress """ eos: GasEOS transport: TransportModel wall_model: PorousWallProperties - def decomposition_progress(self, material_densities) -> DOFArray: - r"""Evaluate the progress ratio $\tau$ of the decomposition. - - Where $\tau=1$, the material is locally virgin. On the other hand, if - $\tau=0$, then the fibers were all consumed. - """ - mass = self.solid_density(material_densities) - return self.wall_model.decomposition_progress(mass) - def solid_density(self, material_densities) -> DOFArray: r"""Return the solid density $\epsilon_s \rho_s$. @@ -313,6 +319,15 @@ def solid_density(self, material_densities) -> DOFArray: return material_densities return sum(material_densities) + def decomposition_progress(self, material_densities) -> DOFArray: + r"""Evaluate the progress ratio $\tau$ of the decomposition. + + Where $\tau=1$, the material is locally virgin. On the other hand, if + $\tau=0$, then the fibers were all consumed. + """ + mass = self.solid_density(material_densities) + return self.wall_model.decomposition_progress(mass) + def get_temperature(self, cv: ConservedVars, wv: PorousWallVars, tseed: DOFArray, niter=3) -> DOFArray: r"""Evaluate the temperature based on solid+gas properties. @@ -345,18 +360,36 @@ def get_temperature(self, cv: ConservedVars, wv: PorousWallVars, def get_pressure(self, cv: ConservedVars, wv: PorousWallVars, temperature: DOFArray) -> DOFArray: - """Return the pressure of the gas considering the void fraction.""" + r"""Return the pressure of the gas considering the void fraction. + + Since the density is evaluated based on the entire bulk material, i.e., + considering the void fraction, the pressure is evaluated as + + .. math:: + P = \frac{\epsilon \rho}{\epsilon} \frac{R}{M} T + + where $\epsilon \rho$ is stored in :class:`~mirgecom.fluid.ConservedVars` + and $M$ is the molar mass of the mixture. + """ return self.eos.pressure(cv, temperature)/wv.void_fraction def internal_energy(self, cv: ConservedVars, wv: PorousWallVars, temperature: DOFArray) -> DOFArray: - """Return the enthalpy of the gas+solid material.""" + r"""Return the enthalpy of the gas+solid material. + + .. math:: + \rho e = \epsilon_s \rho_s e_s + \epsilon_g \rho_g e_g + """ return (cv.mass*self.eos.get_internal_energy(temperature, cv.species_mass_fractions) + wv.density*self.wall_model.enthalpy(temperature, wv.tau)) def heat_capacity(self, cv: ConservedVars, wv: PorousWallVars, temperature: DOFArray) -> DOFArray: - """Return the heat capacity of the gas+solid material.""" + r"""Return the heat capacity of the gas+solid material. + + .. math:: + \rho e = \epsilon_s \rho_s {C_p}_s + \epsilon_g \rho_g {C_v}_g + """ return (cv.mass*self.eos.heat_capacity_cv(cv, temperature) + wv.density*self.wall_model.heat_capacity(temperature, wv.tau)) From 3c952b0b4a6f0b4bfa4e36c7011d28da46be5f8b Mon Sep 17 00:00:00 2001 From: Tulio Date: Tue, 25 Jul 2023 10:10:27 -0500 Subject: [PATCH 25/45] pydocstyle --- mirgecom/wall_model.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mirgecom/wall_model.py b/mirgecom/wall_model.py index 1a3ee3626..bc5d0eed1 100644 --- a/mirgecom/wall_model.py +++ b/mirgecom/wall_model.py @@ -150,7 +150,7 @@ def dependent_vars(self, wv: SolidWallConservedVars, @dataclass_array_container @dataclass(frozen=True, eq=False) class PorousWallVars: - """Variables for the (porous) fluid state. + """Variables for the porous material. .. attribute:: material_densities .. attribute:: tau @@ -271,9 +271,9 @@ def heat_capacity(self, cv: ConservedVars, wv: PorousWallVars, @dataclass(frozen=True) class PorousFlowModel(PorousFlowEOS): - """ - This is the main class of the porous media flow and is the - equivalent to :class:`~mirgecom.gas_model.GasModel`. It wraps: + """Main class of the porous media flow. + + It is the equivalent to :class:`~mirgecom.gas_model.GasModel` and wraps: .. attribute:: wall_model From 98cb6d6526719466328f48ff3b88adbb0db33f2d Mon Sep 17 00:00:00 2001 From: Tulio Date: Tue, 25 Jul 2023 16:43:49 -0500 Subject: [PATCH 26/45] Add istep for restart example --- examples/ablation-workshop-mpi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/ablation-workshop-mpi.py b/examples/ablation-workshop-mpi.py index 63d67e792..8143d61b0 100644 --- a/examples/ablation-workshop-mpi.py +++ b/examples/ablation-workshop-mpi.py @@ -1116,8 +1116,8 @@ def my_post_step(step, t, dt, state): post_step_callback=my_post_step, dt=current_dt, state=make_obj_array([fluid_state.cv, material_densities, fluid_state.temperature]), - t=current_t, t_final=t_final, force_eval=True) - + t=current_t, t_final=t_final, istep=istep, + force_eval=True) # Dump the final data if rank == 0: logger.info("Checkpointing final state ...") From 57b7735364f465241061c6dfaa371f255df914b9 Mon Sep 17 00:00:00 2001 From: Tulio Date: Thu, 27 Jul 2023 17:43:33 -0500 Subject: [PATCH 27/45] Rename the user-defined material and fix the decomposition progress func --- mirgecom/materials/__init__.py | 2 +- ...{simple_material.py => prescribed_material.py} | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) rename mirgecom/materials/{simple_material.py => prescribed_material.py} (88%) diff --git a/mirgecom/materials/__init__.py b/mirgecom/materials/__init__.py index db33e0df8..e13467cd3 100644 --- a/mirgecom/materials/__init__.py +++ b/mirgecom/materials/__init__.py @@ -28,5 +28,5 @@ .. automodule:: mirgecom.materials.initializer .. automodule:: mirgecom.materials.carbon_fiber .. automodule:: mirgecom.materials.tacot -.. automodule:: mirgecom.materials.simple_material +.. automodule:: mirgecom.materials.prescribed_material """ diff --git a/mirgecom/materials/simple_material.py b/mirgecom/materials/prescribed_material.py similarity index 88% rename from mirgecom/materials/simple_material.py rename to mirgecom/materials/prescribed_material.py index 13fbbda75..d1d4885c0 100644 --- a/mirgecom/materials/simple_material.py +++ b/mirgecom/materials/prescribed_material.py @@ -1,4 +1,4 @@ -""":mod:`mirgecom.materials.simple_material` for user-defined materials.""" +""":mod:`mirgecom.materials.prescribed_material` for user-defined materials.""" __copyright__ = """ Copyright (C) 2023 University of Illinois Board of Trustees @@ -30,7 +30,7 @@ class SolidProperties(PorousWallProperties): - """Evaluate the properties of the solid state containing only fibers. + """Evaluate the properties of a user-defined material. .. automethod:: void_fraction .. automethod:: enthalpy @@ -43,12 +43,10 @@ class SolidProperties(PorousWallProperties): .. automethod:: decomposition_progress """ - def __init__(self, char_mass, virgin_mass, enthalpy_func, heat_capacity_func, - thermal_conductivity_func, volume_fraction_func, permeability_func, - emissivity_func, tortuosity_func): + def __init__(self, enthalpy_func, heat_capacity_func,thermal_conductivity_func, + volume_fraction_func, permeability_func, emissivity_func, tortuosity_func, + decomposition_progress_func): - self._char_mass = char_mass - self._virgin_mass = virgin_mass self._enthalpy_func = enthalpy_func self._heat_capacity_func = heat_capacity_func self._thermal_conductivity_func = thermal_conductivity_func @@ -56,6 +54,7 @@ def __init__(self, char_mass, virgin_mass, enthalpy_func, heat_capacity_func, self._permeability_func = permeability_func self._emissivity_func = emissivity_func self._tortuosity_func = tortuosity_func + self._decomposition_progress_func = decomposition_progress_func def void_fraction(self, tau: DOFArray) -> DOFArray: r"""Return the volumetric fraction $\epsilon$ filled with gas. @@ -106,4 +105,4 @@ def tortuosity(self, tau: DOFArray) -> DOFArray: def decomposition_progress(self, mass: DOFArray) -> DOFArray: r"""Evaluate the progress rate $\tau$.""" - return 1.0 - (self._virgin_mass - mass)/self._virgin_mass + return self._decomposition_progress_func(mass) From ed98da2d39577678123eb31948f20744424468fc Mon Sep 17 00:00:00 2001 From: Tulio Date: Thu, 27 Jul 2023 17:55:50 -0500 Subject: [PATCH 28/45] flake8 --- mirgecom/materials/prescribed_material.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mirgecom/materials/prescribed_material.py b/mirgecom/materials/prescribed_material.py index d1d4885c0..bb5961bb9 100644 --- a/mirgecom/materials/prescribed_material.py +++ b/mirgecom/materials/prescribed_material.py @@ -43,9 +43,9 @@ class SolidProperties(PorousWallProperties): .. automethod:: decomposition_progress """ - def __init__(self, enthalpy_func, heat_capacity_func,thermal_conductivity_func, - volume_fraction_func, permeability_func, emissivity_func, tortuosity_func, - decomposition_progress_func): + def __init__(self, enthalpy_func, heat_capacity_func, thermal_conductivity_func, + volume_fraction_func, permeability_func, emissivity_func, + tortuosity_func, decomposition_progress_func): self._enthalpy_func = enthalpy_func self._heat_capacity_func = heat_capacity_func From 121f6fb621b94795bd7cd2fcb183e561f61e86c5 Mon Sep 17 00:00:00 2001 From: Tulio Date: Thu, 27 Jul 2023 17:59:51 -0500 Subject: [PATCH 29/45] Add blank line to AW example --- examples/ablation-workshop-mpi.py | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/ablation-workshop-mpi.py b/examples/ablation-workshop-mpi.py index 8143d61b0..07890a156 100644 --- a/examples/ablation-workshop-mpi.py +++ b/examples/ablation-workshop-mpi.py @@ -1118,6 +1118,7 @@ def my_post_step(step, t, dt, state): fluid_state.temperature]), t=current_t, t_final=t_final, istep=istep, force_eval=True) + # Dump the final data if rank == 0: logger.info("Checkpointing final state ...") From f57bde4ed32a694f6ddaa352a18dc2ea1d7f5501 Mon Sep 17 00:00:00 2001 From: Tulio Date: Thu, 17 Aug 2023 15:46:01 -0500 Subject: [PATCH 30/45] Matts review --- doc/index.rst | 1 + doc/mathematical/composite_material.rst | 2 +- doc/operators/thermally_coupled.rst | 47 ------------- doc/wall_model.rst | 23 +++++++ examples/ablation-workshop-mpi.py | 69 ++++++++----------- mirgecom/gas_model.py | 14 ++-- mirgecom/materials/carbon_fiber.py | 4 +- ...erial.py => prescribed_porous_material.py} | 9 ++- mirgecom/materials/tacot.py | 4 +- mirgecom/simutil.py | 21 ------ mirgecom/transport.py | 2 +- mirgecom/utils.py | 37 ++++++++++ mirgecom/wall_model.py | 51 +++++++++++++- 13 files changed, 160 insertions(+), 124 deletions(-) create mode 100644 doc/wall_model.rst rename mirgecom/materials/{prescribed_material.py => prescribed_porous_material.py} (94%) diff --git a/doc/index.rst b/doc/index.rst index 26f606155..f28faca0b 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -72,6 +72,7 @@ Table of Contents mathematical/mathematical discretization + wall_model operators/operators support/support development/development diff --git a/doc/mathematical/composite_material.rst b/doc/mathematical/composite_material.rst index f84d0bd4e..d35ab7c04 100644 --- a/doc/mathematical/composite_material.rst +++ b/doc/mathematical/composite_material.rst @@ -13,7 +13,7 @@ Carbon Fiber Oxidation The temporal evolution of mass density is solved in order to predict the material degradation. As the - :class:`~mirgecom.materials.carbon_fiber.Oxidation` progresses, + :attr:`~mirgecom.materials.carbon_fiber.Oxidation` progresses, the temporal evolution of the fibers mass is given by .. math :: diff --git a/doc/operators/thermally_coupled.rst b/doc/operators/thermally_coupled.rst index cd747b056..3f47dc5f1 100644 --- a/doc/operators/thermally_coupled.rst +++ b/doc/operators/thermally_coupled.rst @@ -2,50 +2,3 @@ Thermally Coupled Fluid-Wall ============================ .. automodule:: mirgecom.multiphysics.thermally_coupled_fluid_wall - -Solid materials -^^^^^^^^^^^^^^^ - -Wall variables --------------- -.. autoclass:: mirgecom.wall_model.SolidWallConservedVars -.. autoclass:: mirgecom.wall_model.SolidWallDependentVars -.. autoclass:: mirgecom.wall_model.SolidWallState - -EOS ---- -.. autoclass:: mirgecom.wall_model.SolidWallModel - -Model-specific properties -------------------------- - The properties are defined exclusively at the driver. - -Porous materials -^^^^^^^^^^^^^^^^ - -Wall variables --------------- -.. autoclass:: mirgecom.wall_model.PorousWallVars - -Porous Media EOS ----------------- -.. autoclass:: mirgecom.wall_model.PorousFlowModel - -Model-specific properties -------------------------- - The properties of the materials are defined in specific files and used by - :class:`~mirgecom.wall_model.PorousWallProperties`. - -.. autoclass:: mirgecom.wall_model.PorousWallProperties - -Carbon fiber -"""""""""""" -.. automodule:: mirgecom.materials.carbon_fiber - -TACOT -""""" -.. automodule:: mirgecom.materials.tacot - -Helper Functions -^^^^^^^^^^^^^^^^ -.. automodule:: mirgecom.materials.initializer diff --git a/doc/wall_model.rst b/doc/wall_model.rst new file mode 100644 index 000000000..a22effb44 --- /dev/null +++ b/doc/wall_model.rst @@ -0,0 +1,23 @@ +Wall Model +========== + +.. automodule:: mirgecom.wall_model + +Material specification +^^^^^^^^^^^^^^^^^^^^^^ + +Carbon fiber +"""""""""""" +.. automodule:: mirgecom.materials.carbon_fiber + +TACOT +""""" +.. automodule:: mirgecom.materials.tacot + +User-defined material +""""""""""""""""""""" +.. automodule:: mirgecom.materials.prescribed_porous_material + +Helper Functions +^^^^^^^^^^^^^^^^ +.. automodule:: mirgecom.materials.initializer diff --git a/examples/ablation-workshop-mpi.py b/examples/ablation-workshop-mpi.py index 9efb963ed..93f70e4d0 100644 --- a/examples/ablation-workshop-mpi.py +++ b/examples/ablation-workshop-mpi.py @@ -1,15 +1,4 @@ -r"""Demonstrate Ablation Workshop case \#2.1. - -.. autoclass:: GasProperties -.. autoclass:: BprimeTable -.. autoclass:: WallTabulatedEOS - -Helper Functions ----------------- -.. autofunction:: eval_spline -.. autofunction:: eval_spline_derivative - -""" +r"""Demonstrate Ablation Workshop case \#2.1.""" __copyright__ = "Copyright (C) 2023 University of Illinois Board of Trustees" @@ -77,7 +66,7 @@ from mirgecom.gas_model import PorousFlowFluidState from mirgecom.wall_model import ( PorousWallVars, - PorousFlowModel as OriginalPorousFlowModel + PorousFlowModel as BasePorousFlowModel ) from mirgecom.fluid import ConservedVars from mirgecom.transport import GasTransportVars @@ -147,7 +136,7 @@ def initializer(dim, gas_model, material_densities, temperature, raise ValueError("Must specify one of 'gas_density' or 'pressure'") if not isinstance(temperature, DOFArray): - raise ValueError("Temperature does not have the proper shape") + raise TypeError("Temperature does not have the proper shape") actx = temperature.array_context @@ -229,27 +218,25 @@ class BprimeTable: portion is evaluated. This is NOT used for fully-coupled cases. """ - def __init__(self): - - import mirgecom - path = mirgecom.__path__[0] + "/materials/aw_Bprime.dat" - bprime_table = \ - (np.genfromtxt(path, skip_header=1)[:, 2:6]).reshape((25, 151, 4)) + import mirgecom + path = mirgecom.__path__[0] + "/materials/aw_Bprime.dat" + bprime_table = \ + (np.genfromtxt(path, skip_header=1)[:, 2:6]).reshape((25, 151, 4)) - # bprime contains: B_g, B_c, Temperature T, Wall enthalpy H_W - self._bounds_T = bprime_table[ 0, :-1:6, 2] # noqa E201 - self._bounds_B = bprime_table[::-1, 0, 0] - self._Bc = bprime_table[::-1, :, 1] - self._Hw = bprime_table[::-1, :-1:6, 3] + # bprime contains: B_g, B_c, Temperature T, Wall enthalpy H_W + bounds_T = bprime_table[0, :-1:6, 2] # noqa N815 + bounds_B = bprime_table[::-1, 0, 0] # noqa N815 + Bc = bprime_table[::-1, :, 1] + Hw = bprime_table[::-1, :-1:6, 3] - # create spline to interpolate the wall enthalpy - self._cs_Hw = np.zeros((25, 4, 24)) - for i in range(0, 25): - self._cs_Hw[i, :, :] = scipy.interpolate.CubicSpline( - self._bounds_T, self._Hw[i, :]).c + # create spline to interpolate the wall enthalpy + cs_Hw = np.zeros((25, 4, 24)) # noqa N815 + for i in range(0, 25): + cs_Hw[i, :, :] = scipy.interpolate.CubicSpline( + bounds_T, Hw[i, :]).c -class GasProperties: +class GasEOS: """Simplified model of the pyrolysis gas using tabulated data. This section is to be used when species conservation is not employed and @@ -397,7 +384,7 @@ def pressure(self, cv: ConservedVars, temperature: DOFArray) -> DOFArray: return cv.mass*gas_const*temperature -class PorousFlowModel(OriginalPorousFlowModel): +class PorousFlowModel(BasePorousFlowModel): """EOS for wall using tabulated data. Inherits WallEOS and add an temperature-evaluation function exclusive @@ -442,7 +429,7 @@ def get_temperature(self, cv, wv, tseed, niter=3): actx = cv.array_context temp = tseed + actx.np.zeros_like(cv.mass) else: - temp = tseed*1.0 + temp = tseed for _ in range(0, niter): eps_rho_e = (cv.mass*self.eos.get_internal_energy(temperature=temp) @@ -611,9 +598,9 @@ def my_wall_bdry(u_minus): import mirgecom.materials.tacot as my_composite - my_gas = GasProperties() + my_gas = GasEOS() bprime_class = BprimeTable() - my_material = my_composite.SolidProperties(char_mass=220.0, virgin_mass=280.0) + my_material = my_composite.TACOTProperties(char_mass=220.0, virgin_mass=280.0) pyrolysis = my_composite.Pyrolysis() gas_model = PorousFlowModel(eos=my_gas, wall_model=my_material, transport=None) @@ -870,8 +857,8 @@ def ablation_workshop_flux(dcoll, state, gas_model, velocity, bprime_class, # ~~~~ # get the wall enthalpy using spline interpolation - bnds_T = bprime_class._bounds_T # noqa N806 - bnds_B = bprime_class._bounds_B # noqa N806 + bnds_T = bprime_class.bounds_T # noqa N806 + bnds_B = bprime_class.bounds_B # noqa N806 # using spline for temperature interpolation # while using "nearest neighbor" for the "B" parameter @@ -884,10 +871,10 @@ def ablation_workshop_flux(dcoll, state, gas_model, velocity, bprime_class, actx.np.where(actx.np.less(temperature_bc, bnds_T[j+1]), actx.np.where(actx.np.greater_equal(Bsurf, bnds_B[k]), actx.np.where(actx.np.less(Bsurf, bnds_B[k+1]), - bprime_class._cs_Hw[k, 0, j]*(temperature_bc-bnds_T[j])**3 - + bprime_class._cs_Hw[k, 1, j]*(temperature_bc-bnds_T[j])**2 - + bprime_class._cs_Hw[k, 2, j]*(temperature_bc-bnds_T[j]) - + bprime_class._cs_Hw[k, 3, j], 0.0), 0.0), 0.0), 0.0) + bprime_class.cs_Hw[k, 0, j]*(temperature_bc-bnds_T[j])**3 + + bprime_class.cs_Hw[k, 1, j]*(temperature_bc-bnds_T[j])**2 + + bprime_class.cs_Hw[k, 2, j]*(temperature_bc-bnds_T[j]) + + bprime_class.cs_Hw[k, 3, j], 0.0), 0.0), 0.0), 0.0) i += 1 h_w = binary_sum(h_w_comps) diff --git a/mirgecom/gas_model.py b/mirgecom/gas_model.py index 8fef434a3..9dd414d5b 100644 --- a/mirgecom/gas_model.py +++ b/mirgecom/gas_model.py @@ -305,7 +305,8 @@ def make_fluid_state(cv, gas_model, The gas conserved state - gas_model: :class:`~mirgecom.gas_model.GasModel` + gas_model: :class:`~mirgecom.gas_model.GasModel` or + :class:`~mirgecom.wall_model.PorousFlowModel` The physical model for the gas/fluid. @@ -354,6 +355,10 @@ def make_fluid_state(cv, gas_model, smoothness_beta = (actx.np.zeros_like(cv.mass) if smoothness_beta is None else smoothness_beta) + if (not isinstance(gas_model, GasModel) + or not isinstance(gas_model, PorousFlowModel)): + raise TypeError("Invalid type for gas_model") + if isinstance(gas_model, GasModel): temperature = gas_model.eos.temperature(cv=cv, temperature_seed=temperature_seed) @@ -390,6 +395,8 @@ def make_fluid_state(cv, gas_model, return FluidState(cv=cv, dv=dv) + # TODO ideally, we want to avoid using "gas model" because the name contradicts + # its usage with solid+fluid. if isinstance(gas_model, PorousFlowModel): # FIXME per previous review, think of a way to de-couple wall and fluid. @@ -407,9 +414,8 @@ def make_fluid_state(cv, gas_model, tortuosity=gas_model.wall_model.tortuosity(tau) ) - # FIXME is there a way to pass the argument to "niter"? temperature = gas_model.get_temperature(cv=cv, wv=wv, - tseed=temperature_seed, niter=3) + tseed=temperature_seed) pressure = gas_model.get_pressure(cv, wv, temperature) @@ -433,8 +439,6 @@ def make_fluid_state(cv, gas_model, return PorousFlowFluidState(cv=cv, dv=dv, tv=tv, wv=wv) - return None - def project_fluid_state(dcoll, src, tgt, state, gas_model, limiter_func=None): """Project a fluid state onto a boundary consistent with the gas model. diff --git a/mirgecom/materials/carbon_fiber.py b/mirgecom/materials/carbon_fiber.py index 0a49eae3b..c899476b6 100644 --- a/mirgecom/materials/carbon_fiber.py +++ b/mirgecom/materials/carbon_fiber.py @@ -1,7 +1,7 @@ r""":mod:`mirgecom.materials.carbon_fiber` evaluate carbon fiber data. .. autoclass:: Oxidation -.. autoclass:: SolidProperties +.. autoclass:: FiberProperties """ __copyright__ = """ @@ -106,7 +106,7 @@ def get_source_terms(self, temperature, tau, rhoY_o2) -> DOFArray: # noqa N803 return (mw_co/mw_o2 + mw_o/mw_o2 - 1)*rhoY_o2*k*eff_surf_area -class SolidProperties(PorousWallProperties): +class FiberProperties(PorousWallProperties): """Evaluate the properties of the solid state containing only fibers. .. automethod:: void_fraction diff --git a/mirgecom/materials/prescribed_material.py b/mirgecom/materials/prescribed_porous_material.py similarity index 94% rename from mirgecom/materials/prescribed_material.py rename to mirgecom/materials/prescribed_porous_material.py index bb5961bb9..2d6079fd3 100644 --- a/mirgecom/materials/prescribed_material.py +++ b/mirgecom/materials/prescribed_porous_material.py @@ -1,4 +1,9 @@ -""":mod:`mirgecom.materials.prescribed_material` for user-defined materials.""" +""":mod:`mirgecom.materials.prescribed_porous_material` for user-defined material. + +The user can create any material with driver-oriented functions. + +.. autoclass:: PrescribedProperties +""" __copyright__ = """ Copyright (C) 2023 University of Illinois Board of Trustees @@ -29,7 +34,7 @@ from mirgecom.wall_model import PorousWallProperties -class SolidProperties(PorousWallProperties): +class PrescribedProperties(PorousWallProperties): """Evaluate the properties of a user-defined material. .. automethod:: void_fraction diff --git a/mirgecom/materials/tacot.py b/mirgecom/materials/tacot.py index e24e532e7..3b02c37b0 100644 --- a/mirgecom/materials/tacot.py +++ b/mirgecom/materials/tacot.py @@ -5,7 +5,7 @@ Ablator) proposed as an open-source material for the Ablation Workshop. .. autoclass:: Pyrolysis -.. autoclass:: SolidProperties +.. autoclass:: TACOTProperties """ __copyright__ = """ @@ -99,7 +99,7 @@ def get_source_terms(self, temperature, chi): actx.np.zeros_like(temperature)]) -class SolidProperties(PorousWallProperties): +class TACOTProperties(PorousWallProperties): """Evaluate the properties of the solid state containing resin and fibers. A linear weighting between the virgin and chared states is applied to diff --git a/mirgecom/simutil.py b/mirgecom/simutil.py index 0f4f2cbf7..a8da2609e 100644 --- a/mirgecom/simutil.py +++ b/mirgecom/simutil.py @@ -22,7 +22,6 @@ Mesh and element utilities -------------------------- -.. autofunction:: mask_from_elements .. autofunction:: geometric_mesh_partitioner .. autofunction:: distribute_mesh .. autofunction:: get_number_of_tetrahedron_nodes @@ -87,26 +86,6 @@ from mpi4py.MPI import Comm -def mask_from_elements(vol_discr, actx, elements): - """Get the individual sub-components from the mesh.""" - mesh = vol_discr.mesh - zeros = vol_discr.zeros(actx) - - group_arrays = [] - - for igrp in range(len(mesh.groups)): - start_elem_nr = mesh.base_element_nrs[igrp] - end_elem_nr = start_elem_nr + mesh.groups[igrp].nelements - grp_elems = elements[ - (elements >= start_elem_nr) - & (elements < end_elem_nr)] - start_elem_nr - grp_ary_np = actx.to_numpy(zeros[igrp]) - grp_ary_np[grp_elems] = 1 - group_arrays.append(actx.from_numpy(grp_ary_np)) - - return DOFArray(actx, tuple(group_arrays)) - - def get_number_of_tetrahedron_nodes(dim, order, include_faces=False): """Get number of nodes (modes) in *dim* Tetrahedron of *order*.""" # number of {nodes, modes} see e.g.: diff --git a/mirgecom/transport.py b/mirgecom/transport.py index 459a23ea9..a8a33848f 100644 --- a/mirgecom/transport.py +++ b/mirgecom/transport.py @@ -705,7 +705,7 @@ class PorousWallTransport(TransportModel): Inherits from (and implements) :class:`TransportModel`. - Takes a any transport model and modify it to consider the interaction + Takes any transport model and modifies it to consider the interaction with the porous materials. .. automethod:: __init__ diff --git a/mirgecom/utils.py b/mirgecom/utils.py index 83123b436..56188a323 100644 --- a/mirgecom/utils.py +++ b/mirgecom/utils.py @@ -5,6 +5,7 @@ .. autofunction:: force_evaluation .. autofunction:: normalize_boundaries .. autofunction:: project_from_base +.. autofunction:: mask_from_elements """ __copyright__ = """ @@ -32,6 +33,11 @@ """ from typing import Optional +from arraycontext import tag_axes +from meshmode.dof_array import DOFArray +from meshmode.transform_metadata import ( + DiscretizationElementAxisTag, + DiscretizationDOFAxisTag) def asdict_shallow(dc_instance) -> dict: @@ -145,3 +151,34 @@ def project_from_base(dcoll, tgt_dd, field): return project(dcoll, tgt_dd_base, tgt_dd, field) else: return field + + +def mask_from_elements(dcoll, dd, actx, elements): + """Get a :class:`~meshmode.dof_array.DOFArray` mask corresponding to *elements*. + + Returns + ------- + mask: :class:`meshmode.dof_array.DOFArray` + A DOF array containing $1$ for elements that are in *elements* and $0$ + for elements that aren't. + """ + discr = dcoll.discr_from_dd(dd) + mesh = discr.mesh + zeros = discr.zeros(actx) + + group_arrays = [] + + for igrp in range(len(mesh.groups)): + start_elem_nr = mesh.base_element_nrs[igrp] + end_elem_nr = start_elem_nr + mesh.groups[igrp].nelements + grp_elems = elements[ + (elements >= start_elem_nr) + & (elements < end_elem_nr)] - start_elem_nr + grp_ary_np = actx.to_numpy(zeros[igrp]) + grp_ary_np[grp_elems] = 1 + group_arrays.append(actx.from_numpy(grp_ary_np)) + + return tag_axes(actx, { + 0: DiscretizationElementAxisTag(), + 1: DiscretizationDOFAxisTag() + }, DOFArray(actx, tuple(group_arrays))) diff --git a/mirgecom/wall_model.py b/mirgecom/wall_model.py index bc5d0eed1..ef0647c01 100644 --- a/mirgecom/wall_model.py +++ b/mirgecom/wall_model.py @@ -1,4 +1,43 @@ -""":mod:`mirgecom.wall_model` handles the EOS for wall model.""" +""":mod:`mirgecom.wall_model` handles solid walls and fluid-wall models. + +Solid materials +^^^^^^^^^^^^^^^ + +Impermeable-wall variables +--------------------------- +.. autoclass:: SolidWallConservedVars +.. autoclass:: SolidWallDependentVars +.. autoclass:: SolidWallState + +Impermeable wall EOS +-------------------- +.. autoclass:: SolidWallModel + +Material properties +------------------- + The material properties are defined exclusively at the driver. The user + must provide density, enthalpy, heat capacity and thermal conductivity of + all constituents and their spatial distribution using + :mod:`~mirgecom.utils.mask_from_elements`. + +Porous materials +^^^^^^^^^^^^^^^^ + +Porous-wall variables +--------------------- +.. autoclass:: PorousWallVars + +Porous Media EOS +---------------- +.. autoclass:: PorousFlowModel + +Material properties +------------------- + The properties of the materials are defined in specific files and used by + :class:`PorousWallProperties`. + +.. autoclass:: PorousWallProperties +""" __copyright__ = """ Copyright (C) 2023 University of Illinois Board of Trustees @@ -152,6 +191,10 @@ def dependent_vars(self, wv: SolidWallConservedVars, class PorousWallVars: """Variables for the porous material. + It combines conserved variables, such as material_densities, with those + that depend on the current status of the wall, which is uniquely defined + based on the material densities. + .. attribute:: material_densities .. attribute:: tau .. attribute:: void_fraction @@ -173,6 +216,9 @@ class PorousWallVars: class PorousWallProperties: """Abstract interface for porous media domains. + The properties are material-dependent and specified in individual files + that inherit this class. + .. automethod:: void_fraction .. automethod:: enthalpy .. automethod:: heat_capacity @@ -304,6 +350,7 @@ class PorousFlowModel(PorousFlowEOS): eos: GasEOS transport: TransportModel wall_model: PorousWallProperties + temperature_iteration = 3 def solid_density(self, material_densities) -> DOFArray: r"""Return the solid density $\epsilon_s \rho_s$. @@ -329,7 +376,7 @@ def decomposition_progress(self, material_densities) -> DOFArray: return self.wall_model.decomposition_progress(mass) def get_temperature(self, cv: ConservedVars, wv: PorousWallVars, - tseed: DOFArray, niter=3) -> DOFArray: + tseed: DOFArray, niter=temperature_iteration) -> DOFArray: r"""Evaluate the temperature based on solid+gas properties. It uses the assumption of thermal equilibrium between solid and fluid. From 933593342e174e39d226c2d22f1b7f913c6d399d Mon Sep 17 00:00:00 2001 From: Tulio Date: Thu, 17 Aug 2023 16:10:00 -0500 Subject: [PATCH 31/45] Use 'and' instead of 'or' --- mirgecom/gas_model.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mirgecom/gas_model.py b/mirgecom/gas_model.py index 9dd414d5b..46e05060f 100644 --- a/mirgecom/gas_model.py +++ b/mirgecom/gas_model.py @@ -356,7 +356,9 @@ def make_fluid_state(cv, gas_model, is None else smoothness_beta) if (not isinstance(gas_model, GasModel) - or not isinstance(gas_model, PorousFlowModel)): + and not isinstance(gas_model, PorousFlowModel)): + print(not isinstance(gas_model, GasModel)) + print(not isinstance(gas_model, PorousFlowModel)) raise TypeError("Invalid type for gas_model") if isinstance(gas_model, GasModel): From 671850b0d892af9da307f9be8cae8e3cdb00cd1f Mon Sep 17 00:00:00 2001 From: Tulio Date: Fri, 18 Aug 2023 16:12:33 -0500 Subject: [PATCH 32/45] Matts review --- examples/ablation-workshop.py | 358 +++++++++++++++++++--------------- mirgecom/eos.py | 8 +- mirgecom/gas_model.py | 11 +- mirgecom/transport.py | 10 +- mirgecom/wall_model.py | 44 +---- 5 files changed, 216 insertions(+), 215 deletions(-) diff --git a/examples/ablation-workshop.py b/examples/ablation-workshop.py index 86e66ff26..6625858ea 100644 --- a/examples/ablation-workshop.py +++ b/examples/ablation-workshop.py @@ -63,15 +63,17 @@ logmgr_add_cl_device_info, logmgr_add_device_memory_usage ) -from mirgecom.eos import MixtureDependentVars +from mirgecom.eos import ( + GasDependentVars, + GasEOS +) from mirgecom.gas_model import PorousFlowFluidState from mirgecom.wall_model import ( PorousWallVars, PorousFlowModel as BasePorousFlowModel ) from mirgecom.fluid import ConservedVars -from mirgecom.transport import GasTransportVars - +from mirgecom.transport import PorousWallTransport from logpyle import IntervalTimer, set_dt from pytools.obj_array import make_obj_array @@ -143,7 +145,7 @@ def initializer(dim, gas_model, material_densities, temperature, tau = gas_model.decomposition_progress(material_densities) - gas_const = 8314.46261815324/gas_model.eos.molar_mass(temperature) + gas_const = gas_model.eos.gas_const(cv=None, temperature=temperature) if gas_density is None: eps_gas = gas_model.wall_model.void_fraction(tau) @@ -152,7 +154,7 @@ def initializer(dim, gas_model, material_densities, temperature, # internal energy (kinetic energy is neglected) eps_rho_solid = sum(material_densities) bulk_energy = ( - eps_rho_gas*gas_model.eos.get_internal_energy(temperature) + eps_rho_gas*gas_model.eos.get_internal_energy(temperature=temperature) + eps_rho_solid*gas_model.wall_model.enthalpy(temperature, tau) ) @@ -219,25 +221,120 @@ class BprimeTable: portion is evaluated. This is NOT used for fully-coupled cases. """ - import mirgecom - path = mirgecom.__path__[0] + "/materials/aw_Bprime.dat" - bprime_table = \ - (np.genfromtxt(path, skip_header=1)[:, 2:6]).reshape((25, 151, 4)) + T_bounds: np.ndarray + B_bounds: np.ndarray + Bc: np.ndarray + Hw: np.ndarray + Hw_cs: np.ndarray + + def __init__(self): + + import mirgecom + path = mirgecom.__path__[0] + "/materials/aw_Bprime.dat" + bprime_table = \ + (np.genfromtxt(path, skip_header=1)[:, 2:6]).reshape((25, 151, 4)) + + # bprime contains: B_g, B_c, Temperature T, Wall enthalpy H_W + self.T_bounds = bprime_table[0, :-1:6, 2] + self.B_bounds = bprime_table[::-1, 0, 0] + self.Bc = bprime_table[::-1, :, 1] + self.Hw = bprime_table[::-1, :-1:6, 3] + + # create spline to interpolate the wall enthalpy + self.Hw_cs = np.zeros((25, 4, 24)) + for i in range(0, 25): + self.Hw_cs[i, :, :] = \ + scipy.interpolate.CubicSpline(self.T_bounds, self.Hw[i, :]).c + + +class GasTabulatedTransport: + """Evaluate tabulated transport data for TACOT.""" + + def __init__(self, prandtl=1.0, lewis=1.0): + """Return gas tabulated data and interpolating functions. + + Parameters + ---------- + prandtl: float + the Prandtl number of the mixture. Defaults to 1. + lewis: float + the Lewis number of the mixture. Defaults to 1. + """ + self._prandtl = prandtl + self._lewis = lewis - # bprime contains: B_g, B_c, Temperature T, Wall enthalpy H_W - bounds_T = bprime_table[0, :-1:6, 2] # noqa N815 - bounds_B = bprime_table[::-1, 0, 0] # noqa N815 - Bc = bprime_table[::-1, :, 1] - Hw = bprime_table[::-1, :-1:6, 3] + # T , viscosity + gas_data = np.array([ + [200.00, 0.086881], + [350.00, 0.144380], + [500.00, 0.196150], + [650.00, 0.243230], + [700.00, 0.258610], + [750.00, 0.274430], + [800.00, 0.290920], + [850.00, 0.307610], + [900.00, 0.323490], + [975.00, 0.344350], + [1025.0, 0.356630], + [1100.0, 0.373980], + [1150.0, 0.385360], + [1175.0, 0.391330], + [1200.0, 0.397930], + [1275.0, 0.421190], + [1400.0, 0.458870], + [1525.0, 0.483230], + [1575.0, 0.487980], + [1625.0, 0.491950], + [1700.0, 0.502120], + [1775.0, 0.516020], + [1925.0, 0.545280], + [2000.0, 0.559860], + [2150.0, 0.588820], + [2300.0, 0.617610], + [2450.0, 0.646380], + [2600.0, 0.675410], + [2750.0, 0.705000], + [2900.0, 0.735570], + [3050.0, 0.767590], + [3200.0, 0.801520], + [3350.0, 0.837430], + ]) - # create spline to interpolate the wall enthalpy - cs_Hw = np.zeros((25, 4, 24)) # noqa N815 - for i in range(0, 25): - cs_Hw[i, :, :] = scipy.interpolate.CubicSpline( - bounds_T, Hw[i, :]).c + self._cs_viscosity = CubicSpline(gas_data[:, 0], gas_data[:, 1]*1e-4) + def bulk_viscosity(self, cv, dv, eos) -> DOFArray: + r"""Get the bulk viscosity for the gas, $\mu_{B}$.""" + actx = cv.mass.array_context + return actx.np.zeros_like(cv.mass) -class GasEOS: + def volume_viscosity(self, cv, dv, eos) -> DOFArray: + r"""Get the 2nd viscosity coefficent, $\lambda$.""" + return (self.bulk_viscosity(cv, dv, eos) - 2./3.)*self.viscosity(cv, dv, eos) + + def viscosity(self, cv, dv, eos) -> DOFArray: + r"""Return the gas viscosity $\mu$.""" + coeffs = self._cs_viscosity.c + bnds = self._cs_viscosity.x + return eval_spline(dv.temperature, bnds, coeffs) + + def thermal_conductivity(self, cv, dv, eos) -> DOFArray: + r"""Return the gas thermal conductivity $\kappa_g$. + + .. math:: + \kappa = \frac{\mu C_p}{Pr} + + with gas viscosity $\mu$, heat capacity at constant pressure $C_p$ + and the Prandtl number $Pr$ (default to 1). + """ + cp = eos.heat_capacity_cp(cv, dv.temperature) + mu = self.viscosity(cv, dv, eos) + return mu*cp/self._prandtl + + def species_diffusivity(self, cv, dv, eos) -> DOFArray: + return cv.species_mass # empty array + + +class TabulatedGasEOS(GasEOS): """Simplified model of the pyrolysis gas using tabulated data. This section is to be used when species conservation is not employed and @@ -252,71 +349,52 @@ class GasEOS: .. automethod:: get_internal_energy .. automethod:: heat_capacity_cp .. automethod:: heat_capacity_cv - .. automethod:: viscosity - .. automethod:: thermal_conductivity .. automethod:: pressure """ - def __init__(self, prandtl=1.0, lewis=1.0): - """Return gas tabulated data and interpolating functions. + def __init__(self): + """Return gas tabulated data and interpolating functions.""" - Parameters - ---------- - prandtl: float - the Prandtl number of the mixture. Defaults to 1. - lewis: float - the Lewis number of the mixture. Defaults to 1. - """ - self._prandtl = prandtl - self._lewis = lewis - - # T , M , Cp , gamma , enthalpy, viscosity + # T , M , Cp , gamma , enthalpy gas_data = np.array([ - [200.00, 21.996, 1.5119, 1.3334, -7246.50, 0.086881], - [350.00, 21.995, 1.7259, 1.2807, -7006.30, 0.144380], - [500.00, 21.948, 2.2411, 1.2133, -6715.20, 0.196150], - [650.00, 21.418, 4.3012, 1.1440, -6265.70, 0.243230], - [700.00, 20.890, 6.3506, 1.1242, -6004.60, 0.258610], - [750.00, 19.990, 9.7476, 1.1131, -5607.70, 0.274430], - [800.00, 18.644, 14.029, 1.1116, -5014.40, 0.290920], - [850.00, 17.004, 17.437, 1.1171, -4218.50, 0.307610], - [900.00, 15.457, 17.009, 1.1283, -3335.30, 0.323490], - [975.00, 14.119, 8.5576, 1.1620, -2352.90, 0.344350], - [1025.0, 13.854, 4.7840, 1.1992, -2034.20, 0.356630], - [1100.0, 13.763, 3.5092, 1.2240, -1741.20, 0.373980], - [1150.0, 13.737, 3.9008, 1.2087, -1560.90, 0.385360], - [1175.0, 13.706, 4.8067, 1.1899, -1453.50, 0.391330], - [1200.0, 13.639, 6.2353, 1.1737, -1315.90, 0.397930], - [1275.0, 13.256, 8.4790, 1.1633, -739.700, 0.421190], - [1400.0, 12.580, 9.0239, 1.1583, 353.3100, 0.458870], - [1525.0, 11.982, 11.516, 1.1377, 1608.400, 0.483230], - [1575.0, 11.732, 12.531, 1.1349, 2214.000, 0.487980], - [1625.0, 11.495, 11.514, 1.1444, 2826.800, 0.491950], - [1700.0, 11.255, 7.3383, 1.1849, 3529.400, 0.502120], - [1775.0, 11.139, 5.3118, 1.2195, 3991.000, 0.516020], - [1925.0, 11.046, 4.2004, 1.2453, 4681.800, 0.545280], - [2000.0, 11.024, 4.0784, 1.2467, 4991.300, 0.559860], - [2150.0, 10.995, 4.1688, 1.2382, 5605.400, 0.588820], - [2300.0, 10.963, 4.5727, 1.2214, 6257.300, 0.617610], - [2450.0, 10.914, 5.3049, 1.2012, 6993.500, 0.646380], - [2600.0, 10.832, 6.4546, 1.1815, 7869.600, 0.675410], - [2750.0, 10.701, 8.1450, 1.1650, 8956.900, 0.705000], - [2900.0, 10.503, 10.524, 1.1528, 10347.00, 0.735570], - [3050.0, 10.221, 13.755, 1.1449, 12157.00, 0.767590], - [3200.0, 9.8394, 17.957, 1.1408, 14523.00, 0.801520], - [3350.0, 9.3574, 22.944, 1.1401, 17584.00, 0.837430], + [200.00, 21.996, 1.5119, 1.3334, -7246.50], + [350.00, 21.995, 1.7259, 1.2807, -7006.30], + [500.00, 21.948, 2.2411, 1.2133, -6715.20], + [650.00, 21.418, 4.3012, 1.1440, -6265.70], + [700.00, 20.890, 6.3506, 1.1242, -6004.60], + [750.00, 19.990, 9.7476, 1.1131, -5607.70], + [800.00, 18.644, 14.029, 1.1116, -5014.40], + [850.00, 17.004, 17.437, 1.1171, -4218.50], + [900.00, 15.457, 17.009, 1.1283, -3335.30], + [975.00, 14.119, 8.5576, 1.1620, -2352.90], + [1025.0, 13.854, 4.7840, 1.1992, -2034.20], + [1100.0, 13.763, 3.5092, 1.2240, -1741.20], + [1150.0, 13.737, 3.9008, 1.2087, -1560.90], + [1175.0, 13.706, 4.8067, 1.1899, -1453.50], + [1200.0, 13.639, 6.2353, 1.1737, -1315.90], + [1275.0, 13.256, 8.4790, 1.1633, -739.700], + [1400.0, 12.580, 9.0239, 1.1583, 353.3100], + [1525.0, 11.982, 11.516, 1.1377, 1608.400], + [1575.0, 11.732, 12.531, 1.1349, 2214.000], + [1625.0, 11.495, 11.514, 1.1444, 2826.800], + [1700.0, 11.255, 7.3383, 1.1849, 3529.400], + [1775.0, 11.139, 5.3118, 1.2195, 3991.000], + [1925.0, 11.046, 4.2004, 1.2453, 4681.800], + [2000.0, 11.024, 4.0784, 1.2467, 4991.300], + [2150.0, 10.995, 4.1688, 1.2382, 5605.400], + [2300.0, 10.963, 4.5727, 1.2214, 6257.300], + [2450.0, 10.914, 5.3049, 1.2012, 6993.500], + [2600.0, 10.832, 6.4546, 1.1815, 7869.600], + [2750.0, 10.701, 8.1450, 1.1650, 8956.900], + [2900.0, 10.503, 10.524, 1.1528, 10347.00], + [3050.0, 10.221, 13.755, 1.1449, 12157.00], + [3200.0, 9.8394, 17.957, 1.1408, 14523.00], + [3350.0, 9.3574, 22.944, 1.1401, 17584.00], ]) self._cs_molar_mass = CubicSpline(gas_data[:, 0], gas_data[:, 1]) self._cs_gamma = CubicSpline(gas_data[:, 0], gas_data[:, 3]) self._cs_enthalpy = CubicSpline(gas_data[:, 0], gas_data[:, 4]*1000.0) - self._cs_viscosity = CubicSpline(gas_data[:, 0], gas_data[:, 5]*1e-4) - - def molar_mass(self, temperature: DOFArray) -> DOFArray: - r"""Return the gas molar mass $M$.""" - coeffs = self._cs_molar_mass.c - bnds = self._cs_molar_mass.x - return eval_spline(temperature, bnds, coeffs) def enthalpy(self, temperature: DOFArray) -> DOFArray: r"""Return the gas enthalpy $h_g$.""" @@ -332,10 +410,10 @@ def get_internal_energy(self, temperature: DOFArray) -> DOFArray: .. math:: e(T) = h(T) - \frac{R}{M} T """ - gas_const = 8314.46261815324/self.molar_mass(temperature) + gas_const = self.gas_const(cv=None, temperature=temperature) return self.enthalpy(temperature) - gas_const*temperature - def heat_capacity_cp(self, temperature: DOFArray) -> DOFArray: + def heat_capacity_cp(self, cv: ConservedVars, temperature: DOFArray) -> DOFArray: r"""Return the gas heat capacity at constant pressure $C_{p_g}$. The heat capacity is the derivative of the enthalpy. Thus, to improve @@ -348,48 +426,58 @@ def heat_capacity_cp(self, temperature: DOFArray) -> DOFArray: def heat_capacity_cv(self, cv: ConservedVars, temperature: DOFArray) -> DOFArray: r"""Return the gas heat capacity at constant volume $C_{v_g}$.""" - return self.heat_capacity_cp(temperature)/self.gamma(temperature) + return self.heat_capacity_cp(cv, temperature)/self.gamma(cv, temperature) - def gamma(self, temperature: DOFArray) -> DOFArray: + def gamma(self, cv: ConservedVars, temperature: DOFArray) -> DOFArray: r"""Return the heat of capacity ratios $\gamma$.""" coeffs = self._cs_gamma.c bnds = self._cs_gamma.x return eval_spline(temperature, bnds, coeffs) - def viscosity(self, temperature: DOFArray) -> DOFArray: - r"""Return the gas viscosity $\mu$.""" - coeffs = self._cs_viscosity.c - bnds = self._cs_viscosity.x - return eval_spline(temperature, bnds, coeffs) - - def thermal_conductivity(self, temperature: DOFArray) -> DOFArray: - r"""Return the gas thermal conductivity $\kappa_g$. - - .. math:: - \kappa = \frac{\mu C_p}{Pr} - - with gas viscosity $\mu$, heat capacity at constant pressure $C_p$ - and the Prandtl number $Pr$ (default to 1). - """ - cp = self.heat_capacity_cp(temperature) - mu = self.viscosity(temperature) - return mu*cp/self._prandtl - def pressure(self, cv: ConservedVars, temperature: DOFArray) -> DOFArray: r"""Return the gas pressure. .. math:: P = \frac{\epsilon_g \rho_g}{\epsilon_g} \frac{R}{M} T """ - gas_const = 8314.46261815324/self.molar_mass(temperature) + gas_const = self.gas_const(cv, temperature) return cv.mass*gas_const*temperature + def gas_const(self, cv: ConservedVars, temperature) -> DOFArray: + coeffs = self._cs_molar_mass.c + bnds = self._cs_molar_mass.x + molar_mass = eval_spline(temperature, bnds, coeffs) + return 8314.46261815324/molar_mass + + def temperature(self, cv: ConservedVars, temperature_seed=None): + raise NotImplementedError + + def sound_speed(self, cv: ConservedVars, temperature: DOFArray): + raise NotImplementedError + + def internal_energy(self, cv: ConservedVars): + raise NotImplementedError + + def total_energy(self, cv: ConservedVars, pressure: DOFArray, + temperature: DOFArray): + raise NotImplementedError + + def kinetic_energy(self, cv: ConservedVars): + raise NotImplementedError + + def get_density(self, pressure, temperature, species_mass_fractions=None): + raise NotImplementedError + + def dependent_vars(self, cv: ConservedVars, temperature_seed=None, + smoothness_mu=None, smoothness_kappa=None, smoothness_beta=None): + raise NotImplementedError + class PorousFlowModel(BasePorousFlowModel): """EOS for wall using tabulated data. - Inherits WallEOS and add an temperature-evaluation function exclusive - for TACOT-tabulated data. + Inherits :mod:`~mirgecom.wall_model.PorousFlowModel` and add an + temperature-evaluation function exclusive for TACOT-tabulated data. """ def get_temperature(self, cv, wv, tseed, niter=3): @@ -405,6 +493,8 @@ def get_temperature(self, cv, wv, tseed, niter=3): {\epsilon_g \rho_g e_g + \rho_s h_s - \rho e} {\epsilon_g \rho_g C_{v_g} + \epsilon_s \rho_s C_{p_s}} + Note that kinetic energy is neglected is this case. + Parameters ---------- cv: ConservedVars @@ -453,38 +543,6 @@ def pressure_diffusivity(self, cv: ConservedVars, wv: PorousWallVars, """ return cv.mass*wv.permeability/(viscosity*wv.void_fraction) - def viscosity(self, wv: PorousWallVars, temperature: DOFArray, - gas_tv: GasTransportVars) -> DOFArray: - """Viscosity of the gas through the (porous) wall.""" - return gas_tv.viscosity/wv.void_fraction - - def thermal_conductivity(self, cv: ConservedVars, - wv: PorousWallVars, - temperature: DOFArray, - gas_tv: GasTransportVars) -> DOFArray: - r"""Return the effective thermal conductivity of the gas+solid. - - It is a function of temperature and degradation progress. As the - fibers are oxidized, they reduce their cross area and, consequently, - their ability to conduct heat. - - It is evaluated using a mass-weighted average given by - - .. math:: - \frac{\rho_s \kappa_s + \rho_g \kappa_g}{\rho_s + \rho_g} - """ - y_g = cv.mass/(cv.mass + wv.density) - y_s = 1.0 - y_g - kappa_s = self.wall_model.thermal_conductivity(temperature, wv.tau) - kappa_g = gas_tv.thermal_conductivity - - return y_s*kappa_s + y_g*kappa_g - - def species_diffusivity(self, wv: PorousWallVars, - temperature: DOFArray, gas_tv: GasTransportVars) -> DOFArray: - """Mass diffusivity of gaseous species through the (porous) wall.""" - return gas_tv.species_diffusivity/wv.tortuosity - def binary_sum(ary): """Sum the elements of an array, creating a log-depth DAG instead of linear.""" @@ -599,12 +657,15 @@ def my_wall_bdry(u_minus): import mirgecom.materials.tacot as my_composite - my_gas = GasEOS() + my_gas = TabulatedGasEOS() bprime_class = BprimeTable() my_material = my_composite.TACOTProperties(char_mass=220.0, virgin_mass=280.0) pyrolysis = my_composite.Pyrolysis() - gas_model = PorousFlowModel(eos=my_gas, wall_model=my_material, transport=None) + base_transport = GasTabulatedTransport() + sample_transport = PorousWallTransport(base_transport=base_transport) + gas_model = PorousFlowModel(eos=my_gas, wall_model=my_material, + transport=sample_transport) # }}} @@ -668,36 +729,17 @@ def make_state(cv, temperature_seed, material_densities): pressure = gas_model.get_pressure(cv=cv, wv=wv, temperature=temperature) - dv = MixtureDependentVars( + dv = GasDependentVars( temperature=temperature, pressure=pressure, speed_of_sound=zeros, smoothness_mu=zeros, smoothness_kappa=zeros, - smoothness_beta=zeros, - species_enthalpies=cv.species_mass, + smoothness_beta=zeros ) - # gas only transport vars - gas_tv = GasTransportVars( - bulk_viscosity=zeros, - viscosity=my_gas.viscosity(temperature), - thermal_conductivity=my_gas.thermal_conductivity(temperature), - species_diffusivity=cv.species_mass) - - # coupled solid-gas thermal conductivity - kappa = gas_model.thermal_conductivity(cv, wv, temperature, gas_tv) - - # coupled solid-gas viscosity - viscosity = gas_model.viscosity(wv, temperature, gas_tv) - - # return modified transport vars - tv = GasTransportVars( - bulk_viscosity=zeros, - viscosity=viscosity, - thermal_conductivity=kappa, - species_diffusivity=cv.species_mass - ) + tv = gas_model.transport.transport_vars(cv=cv, dv=dv, wv=wv, + flow_model=gas_model) return PorousFlowFluidState(cv=cv, dv=dv, tv=tv, wv=wv) diff --git a/mirgecom/eos.py b/mirgecom/eos.py index 241f64cc2..cdfac9aea 100644 --- a/mirgecom/eos.py +++ b/mirgecom/eos.py @@ -235,7 +235,7 @@ def get_density(self, pressure, temperature, species_mass_fractions): """Get the density from pressure, temperature, and species fractions (Y).""" @abstractmethod - def get_species_molecular_weights(self): + def get_species_molecular_weights(self, temperature: DOFArray): """Get the species molecular weights.""" @abstractmethod @@ -341,7 +341,7 @@ def heat_capacity_cv(self, cv: Optional[ConservedVars] = None, temperature=None) """ return self._gas_const / (self._gamma - 1) - def gas_const(self, cv: Optional[ConservedVars] = None): + def gas_const(self, cv: Optional[ConservedVars] = None, temperature=None): """Get specific gas constant R.""" return self._gas_const @@ -662,7 +662,7 @@ def gamma(self, cv: ConservedVars, temperature): # type: ignore[override] rspec = self.gas_const(cv) return cp / (cp - rspec) - def gas_const(self, cv: ConservedVars): + def gas_const(self, cv: ConservedVars, temperature=None): r"""Get specific gas constant $R_s$. The mixture specific gas constant is calculated @@ -780,7 +780,7 @@ def get_enthalpy(self, temperature, species_mass_fractions): return self._pyrometheus_mech.get_mixture_enthalpy_mass( temperature, species_mass_fractions) - def get_species_molecular_weights(self): + def get_species_molecular_weights(self, temperature=None): """Get the species molecular weights.""" return self._pyrometheus_mech.wts diff --git a/mirgecom/gas_model.py b/mirgecom/gas_model.py index a6a50d728..624add1c6 100644 --- a/mirgecom/gas_model.py +++ b/mirgecom/gas_model.py @@ -358,12 +358,6 @@ def make_fluid_state(cv, gas_model, smoothness_beta = (actx.np.zeros_like(cv.mass) if smoothness_beta is None else smoothness_beta) - if (not isinstance(gas_model, GasModel) - and not isinstance(gas_model, PorousFlowModel)): - print(not isinstance(gas_model, GasModel)) - print(not isinstance(gas_model, PorousFlowModel)) - raise TypeError("Invalid type for gas_model") - if isinstance(gas_model, GasModel): temperature = gas_model.eos.temperature(cv=cv, temperature_seed=temperature_seed) @@ -402,7 +396,7 @@ def make_fluid_state(cv, gas_model, # TODO ideally, we want to avoid using "gas model" because the name contradicts # its usage with solid+fluid. - if isinstance(gas_model, PorousFlowModel): + elif isinstance(gas_model, PorousFlowModel): # FIXME per previous review, think of a way to de-couple wall and fluid. # ~~~ we need to squeeze wall_model in gas_model because this is easily @@ -444,6 +438,9 @@ def make_fluid_state(cv, gas_model, return PorousFlowFluidState(cv=cv, dv=dv, tv=tv, wv=wv) + else: + raise TypeError("Invalid type for gas_model") + def project_fluid_state(dcoll, src, tgt, state, gas_model, limiter_func=None, entropy_stable=False): diff --git a/mirgecom/transport.py b/mirgecom/transport.py index a8a33848f..6801b485a 100644 --- a/mirgecom/transport.py +++ b/mirgecom/transport.py @@ -700,11 +700,11 @@ def species_diffusivity(self, cv: ConservedVars, return self._physical_transport.species_diffusivity(cv, dv, eos) -class PorousWallTransport(TransportModel): +# FIXME: Generalize TransportModel interface to accept state variables +# other than fluid cv +class PorousWallTransport: r"""Transport model for porous media flow. - Inherits from (and implements) :class:`TransportModel`. - Takes any transport model and modifies it to consider the interaction with the porous materials. @@ -772,10 +772,10 @@ def species_diffusivity(self, cv: ConservedVars, # type: ignore[override] # FIXME I have to pass "flow_model" but this class is internal to "flow_model".. # Seems dumb to me but I dont know to access the other classes... - def transport_vars(self, cv: ConservedVars, # type: ignore[override] + def transport_vars(self, cv: ConservedVars, dv: GasDependentVars, wv: PorousWallVars, flow_model: PorousFlowModel) -> GasTransportVars: - r"""Compute the transport properties from the conserved state.""" + r"""Compute the transport properties.""" return GasTransportVars( bulk_viscosity=self.bulk_viscosity(cv, dv, wv, flow_model), viscosity=self.viscosity(cv, dv, wv, flow_model), diff --git a/mirgecom/wall_model.py b/mirgecom/wall_model.py index ef0647c01..6bbdd1c48 100644 --- a/mirgecom/wall_model.py +++ b/mirgecom/wall_model.py @@ -76,7 +76,7 @@ ) from mirgecom.fluid import ConservedVars from mirgecom.eos import GasEOS -from mirgecom.transport import TransportModel +from mirgecom.transport import PorousWallTransport @with_container_arithmetic(bcast_obj_array=False, @@ -277,46 +277,8 @@ def decomposition_progress(self, mass: DOFArray) -> DOFArray: raise NotImplementedError() -class PorousFlowEOS: - """Abstract interface to equation of porous flow class.""" - - @abstractmethod - def decomposition_progress(self, material_densities) -> DOFArray: - r"""Evaluate the progress ratio $\tau$ of the phenolics decomposition.""" - raise NotImplementedError() - - @abstractmethod - def solid_density(self, material_densities) -> DOFArray: - r"""Return the solid density $\epsilon_s \rho_s$.""" - raise NotImplementedError() - - @abstractmethod - def get_temperature(self, cv: ConservedVars, wv: PorousWallVars, - tseed: DOFArray, niter=3) -> DOFArray: - r"""Evaluate the temperature based on solid+gas properties.""" - raise NotImplementedError() - - @abstractmethod - def get_pressure(self, cv: ConservedVars, wv: PorousWallVars, - temperature: DOFArray) -> DOFArray: - """Return the pressure of the gas considering the void fraction.""" - raise NotImplementedError() - - @abstractmethod - def internal_energy(self, cv: ConservedVars, wv: PorousWallVars, - temperature: DOFArray) -> DOFArray: - """Return the enthalpy of the gas+solid material.""" - raise NotImplementedError() - - @abstractmethod - def heat_capacity(self, cv: ConservedVars, wv: PorousWallVars, - temperature: DOFArray) -> DOFArray: - """Return the heat capacity of the gas+solid material.""" - raise NotImplementedError() - - @dataclass(frozen=True) -class PorousFlowModel(PorousFlowEOS): +class PorousFlowModel: """Main class of the porous media flow. It is the equivalent to :class:`~mirgecom.gas_model.GasModel` and wraps: @@ -348,8 +310,8 @@ class PorousFlowModel(PorousFlowEOS): """ eos: GasEOS - transport: TransportModel wall_model: PorousWallProperties + transport: PorousWallTransport temperature_iteration = 3 def solid_density(self, material_densities) -> DOFArray: From 0919ff32eabddb2a14ada92d8a9713cb55d2d8e4 Mon Sep 17 00:00:00 2001 From: Tulio Date: Fri, 18 Aug 2023 17:08:21 -0500 Subject: [PATCH 33/45] Check mypy and circular import --- examples/ablation-workshop.py | 10 +++-- mirgecom/eos.py | 6 ++- mirgecom/gas_model.py | 2 +- mirgecom/transport.py | 84 ----------------------------------- mirgecom/wall_model.py | 80 ++++++++++++++++++++++++++++++++- 5 files changed, 89 insertions(+), 93 deletions(-) diff --git a/examples/ablation-workshop.py b/examples/ablation-workshop.py index 6625858ea..c7785eb4d 100644 --- a/examples/ablation-workshop.py +++ b/examples/ablation-workshop.py @@ -75,7 +75,7 @@ from mirgecom.fluid import ConservedVars from mirgecom.transport import PorousWallTransport from logpyle import IntervalTimer, set_dt - +from typing import Optional from pytools.obj_array import make_obj_array # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -402,7 +402,8 @@ def enthalpy(self, temperature: DOFArray) -> DOFArray: bnds = self._cs_enthalpy.x return eval_spline(temperature, bnds, coeffs) - def get_internal_energy(self, temperature: DOFArray) -> DOFArray: + def get_internal_energy(self, temperature: DOFArray, + species_mass_fractions=None) -> DOFArray: r"""Evaluate the gas internal energy $e$. It is evaluated based on the tabulated enthalpy and molar mass as @@ -428,7 +429,7 @@ def heat_capacity_cv(self, cv: ConservedVars, temperature: DOFArray) -> DOFArray r"""Return the gas heat capacity at constant volume $C_{v_g}$.""" return self.heat_capacity_cp(cv, temperature)/self.gamma(cv, temperature) - def gamma(self, cv: ConservedVars, temperature: DOFArray) -> DOFArray: + def gamma(self, cv: ConservedVars, temperature: Optional[DOFArray] = None) -> DOFArray: r"""Return the heat of capacity ratios $\gamma$.""" coeffs = self._cs_gamma.c bnds = self._cs_gamma.x @@ -443,7 +444,8 @@ def pressure(self, cv: ConservedVars, temperature: DOFArray) -> DOFArray: gas_const = self.gas_const(cv, temperature) return cv.mass*gas_const*temperature - def gas_const(self, cv: ConservedVars, temperature) -> DOFArray: + def gas_const(self, cv: Optional[ConservedVars] = None, + temperature: Optional[DOFArray] = None) -> DOFArray: coeffs = self._cs_molar_mass.c bnds = self._cs_molar_mass.x molar_mass = eval_spline(temperature, bnds, coeffs) diff --git a/mirgecom/eos.py b/mirgecom/eos.py index cdfac9aea..fe183dd67 100644 --- a/mirgecom/eos.py +++ b/mirgecom/eos.py @@ -136,7 +136,8 @@ def sound_speed(self, cv: ConservedVars, temperature: DOFArray): """Get the gas sound speed.""" @abstractmethod - def gas_const(self, cv: ConservedVars): + def gas_const(self, cv: Optional[ConservedVars], + temperature: Optional[DOFArray] = None): r"""Get the specific gas constant ($R_s$).""" @abstractmethod @@ -169,7 +170,8 @@ def get_density(self, pressure, temperature, species_mass_fractions=None): """Get the density from pressure, and temperature.""" @abstractmethod - def get_internal_energy(self, temperature, species_mass_fractions=None): + def get_internal_energy(self, temperature: DOFArray, + species_mass_fractions: DOFArray = None) -> DOFArray: """Get the fluid internal energy from temperature.""" def dependent_vars( diff --git a/mirgecom/gas_model.py b/mirgecom/gas_model.py index 624add1c6..811ddec2f 100644 --- a/mirgecom/gas_model.py +++ b/mirgecom/gas_model.py @@ -434,7 +434,7 @@ def make_fluid_state(cv, gas_model, # FIXME I have to pass "gas_model" but this class is internal to "gas_model" # Seems dumb to me but I dont know to access the other classes... - tv = gas_model.transport.transport_vars(cv, dv, wv, gas_model) + tv = gas_model.transport.transport_vars(cv, dv, wv, gas_model.eos, gas_model.wall_model) return PorousFlowFluidState(cv=cv, dv=dv, tv=tv, wv=wv) diff --git a/mirgecom/transport.py b/mirgecom/transport.py index 6801b485a..8c96dc287 100644 --- a/mirgecom/transport.py +++ b/mirgecom/transport.py @@ -698,87 +698,3 @@ def species_diffusivity(self, cv: ConservedVars, eos: Optional[GasEOS] = None) -> DOFArray: r"""Get the vector of species diffusivities, ${d}_{\alpha}$.""" return self._physical_transport.species_diffusivity(cv, dv, eos) - - -# FIXME: Generalize TransportModel interface to accept state variables -# other than fluid cv -class PorousWallTransport: - r"""Transport model for porous media flow. - - Takes any transport model and modifies it to consider the interaction - with the porous materials. - - .. automethod:: __init__ - .. automethod:: bulk_viscosity - .. automethod:: viscosity - .. automethod:: volume_viscosity - .. automethod:: thermal_conductivity - .. automethod:: species_diffusivity - """ - - from mirgecom.wall_model import PorousWallVars, PorousFlowModel - - def __init__(self, base_transport): - """Initialize transport model.""" - self.base_transport = base_transport - - def bulk_viscosity(self, cv: ConservedVars, # type: ignore[override] - dv: GasDependentVars, wv: PorousWallVars, - flow_model: PorousFlowModel) -> DOFArray: - r"""Get the bulk viscosity for the gas, $\mu_{B}$.""" - return self.base_transport.bulk_viscosity(cv, dv, flow_model.eos) - - def volume_viscosity(self, cv: ConservedVars, # type: ignore[override] - dv: GasDependentVars, wv: PorousWallVars, - flow_model: PorousFlowModel) -> DOFArray: - r"""Get the 2nd viscosity coefficent, $\lambda$.""" - return (self.bulk_viscosity(cv, dv, wv, flow_model) - - 2./3. * self.viscosity(cv, dv, wv, flow_model)) - - def viscosity(self, cv: ConservedVars, # type: ignore[override] - dv: GasDependentVars, wv: PorousWallVars, - flow_model: PorousFlowModel) -> DOFArray: - """Viscosity of the gas through the porous wall.""" - return 1.0/wv.void_fraction*( - self.base_transport.viscosity(cv, dv, flow_model.eos)) - - def thermal_conductivity(self, cv: ConservedVars, # type: ignore[override] - dv: GasDependentVars, wv: PorousWallVars, - flow_model: PorousFlowModel) -> DOFArray: - r"""Return the effective thermal conductivity of the gas+solid. - - It is a function of temperature and degradation progress. As the - fibers are oxidized, they reduce their cross area and, consequently, - their ability to conduct heat. - - It is evaluated using a mass-weighted average given by - - .. math:: - \frac{\rho_s \kappa_s + \rho_g \kappa_g}{\rho_s + \rho_g} - """ - y_g = cv.mass/(cv.mass + wv.density) - y_s = 1.0 - y_g - kappa_s = flow_model.wall_model.thermal_conductivity(dv.temperature, wv.tau) - kappa_g = self.base_transport.thermal_conductivity(cv, dv, flow_model.eos) - - return y_s*kappa_s + y_g*kappa_g - - def species_diffusivity(self, cv: ConservedVars, # type: ignore[override] - dv: GasDependentVars, wv: PorousWallVars, - flow_model: PorousFlowModel) -> DOFArray: - """Mass diffusivity of gaseous species through the porous wall.""" - return 1.0/wv.tortuosity*( - self.base_transport.species_diffusivity(cv, dv, flow_model.eos)) - - # FIXME I have to pass "flow_model" but this class is internal to "flow_model".. - # Seems dumb to me but I dont know to access the other classes... - def transport_vars(self, cv: ConservedVars, - dv: GasDependentVars, wv: PorousWallVars, - flow_model: PorousFlowModel) -> GasTransportVars: - r"""Compute the transport properties.""" - return GasTransportVars( - bulk_viscosity=self.bulk_viscosity(cv, dv, wv, flow_model), - viscosity=self.viscosity(cv, dv, wv, flow_model), - thermal_conductivity=self.thermal_conductivity(cv, dv, wv, flow_model), - species_diffusivity=self.species_diffusivity(cv, dv, wv, flow_model) - ) diff --git a/mirgecom/wall_model.py b/mirgecom/wall_model.py index 6bbdd1c48..7f8e3e5e3 100644 --- a/mirgecom/wall_model.py +++ b/mirgecom/wall_model.py @@ -75,8 +75,8 @@ get_container_context_recursively ) from mirgecom.fluid import ConservedVars -from mirgecom.eos import GasEOS -from mirgecom.transport import PorousWallTransport +from mirgecom.eos import GasEOS, GasDependentVars +from mirgecom.transport import GasTransportVars @with_container_arithmetic(bcast_obj_array=False, @@ -277,6 +277,82 @@ def decomposition_progress(self, mass: DOFArray) -> DOFArray: raise NotImplementedError() +# FIXME: Generalize TransportModel interface to accept state variables +# other than fluid cv +class PorousWallTransport: + r"""Transport model for porous media flow. + + Takes any transport model and modifies it to consider the interaction + with the porous materials. + + .. automethod:: __init__ + .. automethod:: bulk_viscosity + .. automethod:: viscosity + .. automethod:: volume_viscosity + .. automethod:: thermal_conductivity + .. automethod:: species_diffusivity + """ + + def __init__(self, base_transport): + """Initialize transport model.""" + self.base_transport = base_transport + + def bulk_viscosity(self, cv: ConservedVars, dv: GasDependentVars, + wv: PorousWallVars, eos) -> DOFArray: + r"""Get the bulk viscosity for the gas, $\mu_{B}$.""" + return self.base_transport.bulk_viscosity(cv, dv, eos) + + def volume_viscosity(self, cv: ConservedVars, dv: GasDependentVars, + wv: PorousWallVars, eos) -> DOFArray: + r"""Get the 2nd viscosity coefficent, $\lambda$.""" + return (self.bulk_viscosity(cv, dv, wv, eos) + - 2./3. * self.viscosity(cv, dv, wv, eos)) + + def viscosity(self, cv: ConservedVars, dv: GasDependentVars, + wv: PorousWallVars, eos) -> DOFArray: + """Viscosity of the gas through the porous wall.""" + return 1.0/wv.void_fraction*( + self.base_transport.viscosity(cv, dv, eos)) + + def thermal_conductivity(self, cv: ConservedVars, dv: GasDependentVars, + wv: PorousWallVars, eos, wall_model) -> DOFArray: + r"""Return the effective thermal conductivity of the gas+solid. + + It is a function of temperature and degradation progress. As the + fibers are oxidized, they reduce their cross area and, consequently, + their ability to conduct heat. + + It is evaluated using a mass-weighted average given by + + .. math:: + \frac{\rho_s \kappa_s + \rho_g \kappa_g}{\rho_s + \rho_g} + """ + y_g = cv.mass/(cv.mass + wv.density) + y_s = 1.0 - y_g + kappa_s = wall_model.thermal_conductivity(dv.temperature, wv.tau) + kappa_g = self.base_transport.thermal_conductivity(cv, dv, eos) + + return y_s*kappa_s + y_g*kappa_g + + def species_diffusivity(self, cv: ConservedVars, dv: GasDependentVars, + wv: PorousWallVars, eos) -> DOFArray: + """Mass diffusivity of gaseous species through the porous wall.""" + return 1.0/wv.tortuosity*( + self.base_transport.species_diffusivity(cv, dv, eos)) + + # FIXME I have to pass "flow_model" but this class is internal to "flow_model".. + # Seems dumb to me but I dont know to access the other classes... + def transport_vars(self, cv: ConservedVars, dv: GasDependentVars, + wv: PorousWallVars, eos, wall_model) -> GasTransportVars: + r"""Compute the transport properties.""" + return GasTransportVars( + bulk_viscosity=self.bulk_viscosity(cv, dv, wv, eos), + viscosity=self.viscosity(cv, dv, wv, eos), + thermal_conductivity=self.thermal_conductivity(cv, dv, wv, eos, wall_model), + species_diffusivity=self.species_diffusivity(cv, dv, wv, eos) + ) + + @dataclass(frozen=True) class PorousFlowModel: """Main class of the porous media flow. From a7934bb4f692d736c3ffb79891cc655e9012d41e Mon Sep 17 00:00:00 2001 From: Tulio Date: Fri, 18 Aug 2023 17:43:10 -0500 Subject: [PATCH 34/45] flake8, mypy, pylint etc etc --- examples/ablation-workshop.py | 54 +++++++++++++++++++++++------------ mirgecom/gas_model.py | 3 +- mirgecom/transport.py | 1 - mirgecom/wall_model.py | 36 ++++++++++++++--------- 4 files changed, 60 insertions(+), 34 deletions(-) diff --git a/examples/ablation-workshop.py b/examples/ablation-workshop.py index c7785eb4d..b86c33812 100644 --- a/examples/ablation-workshop.py +++ b/examples/ablation-workshop.py @@ -64,18 +64,18 @@ logmgr_add_device_memory_usage ) from mirgecom.eos import ( - GasDependentVars, - GasEOS + MixtureDependentVars, + MixtureEOS ) from mirgecom.gas_model import PorousFlowFluidState from mirgecom.wall_model import ( PorousWallVars, - PorousFlowModel as BasePorousFlowModel + PorousFlowModel as BasePorousFlowModel, + PorousWallTransport ) from mirgecom.fluid import ConservedVars -from mirgecom.transport import PorousWallTransport from logpyle import IntervalTimer, set_dt -from typing import Optional +from typing import Optional, Union from pytools.obj_array import make_obj_array # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -334,7 +334,7 @@ def species_diffusivity(self, cv, dv, eos) -> DOFArray: return cv.species_mass # empty array -class TabulatedGasEOS(GasEOS): +class TabulatedGasEOS(MixtureEOS): """Simplified model of the pyrolysis gas using tabulated data. This section is to be used when species conservation is not employed and @@ -429,7 +429,8 @@ def heat_capacity_cv(self, cv: ConservedVars, temperature: DOFArray) -> DOFArray r"""Return the gas heat capacity at constant volume $C_{v_g}$.""" return self.heat_capacity_cp(cv, temperature)/self.gamma(cv, temperature) - def gamma(self, cv: ConservedVars, temperature: Optional[DOFArray] = None) -> DOFArray: + def gamma(self, cv: ConservedVars, + temperature: Optional[DOFArray] = None) -> DOFArray: r"""Return the heat of capacity ratios $\gamma$.""" coeffs = self._cs_gamma.c bnds = self._cs_gamma.x @@ -467,7 +468,23 @@ def total_energy(self, cv: ConservedVars, pressure: DOFArray, def kinetic_energy(self, cv: ConservedVars): raise NotImplementedError - def get_density(self, pressure, temperature, species_mass_fractions=None): + def get_temperature_seed(self, cv: ConservedVars, + temperature_seed: Optional[Union[float, DOFArray]] = None) -> DOFArray: + raise NotImplementedError + + def get_density(self, pressure, temperature, species_mass_fractions): + raise NotImplementedError + + def get_species_molecular_weights(self, temperature: DOFArray): + raise NotImplementedError + + def species_enthalpies(self, cv: ConservedVars, temperature: DOFArray): + raise NotImplementedError + + def get_production_rates(self, cv: ConservedVars, temperature: DOFArray): + raise NotImplementedError + + def get_species_source_terms(self, cv: ConservedVars, temperature: DOFArray): raise NotImplementedError def dependent_vars(self, cv: ConservedVars, temperature_seed=None, @@ -731,17 +748,18 @@ def make_state(cv, temperature_seed, material_densities): pressure = gas_model.get_pressure(cv=cv, wv=wv, temperature=temperature) - dv = GasDependentVars( + dv = MixtureDependentVars( temperature=temperature, pressure=pressure, speed_of_sound=zeros, smoothness_mu=zeros, smoothness_kappa=zeros, - smoothness_beta=zeros + smoothness_beta=zeros, + species_enthalpies=cv.species_mass, # empty array ) - tv = gas_model.transport.transport_vars(cv=cv, dv=dv, wv=wv, - flow_model=gas_model) + tv = gas_model.transport.transport_vars( + cv=cv, dv=dv, wv=wv, eos=gas_model.eos, wall_model=gas_model.wall_model) return PorousFlowFluidState(cv=cv, dv=dv, tv=tv, wv=wv) @@ -902,8 +920,8 @@ def ablation_workshop_flux(dcoll, state, gas_model, velocity, bprime_class, # ~~~~ # get the wall enthalpy using spline interpolation - bnds_T = bprime_class.bounds_T # noqa N806 - bnds_B = bprime_class.bounds_B # noqa N806 + bnds_T = bprime_class.T_bounds # noqa N806 + bnds_B = bprime_class.B_bounds # noqa N806 # using spline for temperature interpolation # while using "nearest neighbor" for the "B" parameter @@ -916,10 +934,10 @@ def ablation_workshop_flux(dcoll, state, gas_model, velocity, bprime_class, actx.np.where(actx.np.less(temperature_bc, bnds_T[j+1]), actx.np.where(actx.np.greater_equal(Bsurf, bnds_B[k]), actx.np.where(actx.np.less(Bsurf, bnds_B[k+1]), - bprime_class.cs_Hw[k, 0, j]*(temperature_bc-bnds_T[j])**3 - + bprime_class.cs_Hw[k, 1, j]*(temperature_bc-bnds_T[j])**2 - + bprime_class.cs_Hw[k, 2, j]*(temperature_bc-bnds_T[j]) - + bprime_class.cs_Hw[k, 3, j], 0.0), 0.0), 0.0), 0.0) + bprime_class.Hw_cs[k, 0, j]*(temperature_bc-bnds_T[j])**3 + + bprime_class.Hw_cs[k, 1, j]*(temperature_bc-bnds_T[j])**2 + + bprime_class.Hw_cs[k, 2, j]*(temperature_bc-bnds_T[j]) + + bprime_class.Hw_cs[k, 3, j], 0.0), 0.0), 0.0), 0.0) i += 1 h_w = binary_sum(h_w_comps) diff --git a/mirgecom/gas_model.py b/mirgecom/gas_model.py index 811ddec2f..242826fcd 100644 --- a/mirgecom/gas_model.py +++ b/mirgecom/gas_model.py @@ -434,7 +434,8 @@ def make_fluid_state(cv, gas_model, # FIXME I have to pass "gas_model" but this class is internal to "gas_model" # Seems dumb to me but I dont know to access the other classes... - tv = gas_model.transport.transport_vars(cv, dv, wv, gas_model.eos, gas_model.wall_model) + tv = gas_model.transport.transport_vars(cv, dv, wv, gas_model.eos, + gas_model.wall_model) return PorousFlowFluidState(cv=cv, dv=dv, tv=tv, wv=wv) diff --git a/mirgecom/transport.py b/mirgecom/transport.py index 8c96dc287..7707c3e90 100644 --- a/mirgecom/transport.py +++ b/mirgecom/transport.py @@ -16,7 +16,6 @@ .. autoclass:: MixtureAveragedTransport .. autoclass:: ArtificialViscosityTransportDiv .. autoclass:: ArtificialViscosityTransportDiv2 -.. autoclass:: PorousWallTransport Exceptions ^^^^^^^^^^ diff --git a/mirgecom/wall_model.py b/mirgecom/wall_model.py index 7f8e3e5e3..b958ec191 100644 --- a/mirgecom/wall_model.py +++ b/mirgecom/wall_model.py @@ -31,6 +31,10 @@ ---------------- .. autoclass:: PorousFlowModel +Porous Media Transport +---------------------- +.. autoclass:: PorousWallTransport + Material properties ------------------- The properties of the materials are defined in specific files and used by @@ -75,7 +79,11 @@ get_container_context_recursively ) from mirgecom.fluid import ConservedVars -from mirgecom.eos import GasEOS, GasDependentVars +from mirgecom.eos import ( + GasEOS, + MixtureEOS, + GasDependentVars +) from mirgecom.transport import GasTransportVars @@ -298,24 +306,25 @@ def __init__(self, base_transport): self.base_transport = base_transport def bulk_viscosity(self, cv: ConservedVars, dv: GasDependentVars, - wv: PorousWallVars, eos) -> DOFArray: + wv: PorousWallVars, eos: GasEOS) -> DOFArray: r"""Get the bulk viscosity for the gas, $\mu_{B}$.""" return self.base_transport.bulk_viscosity(cv, dv, eos) def volume_viscosity(self, cv: ConservedVars, dv: GasDependentVars, - wv: PorousWallVars, eos) -> DOFArray: + wv: PorousWallVars, eos: GasEOS) -> DOFArray: r"""Get the 2nd viscosity coefficent, $\lambda$.""" return (self.bulk_viscosity(cv, dv, wv, eos) - 2./3. * self.viscosity(cv, dv, wv, eos)) def viscosity(self, cv: ConservedVars, dv: GasDependentVars, - wv: PorousWallVars, eos) -> DOFArray: + wv: PorousWallVars, eos: GasEOS) -> DOFArray: """Viscosity of the gas through the porous wall.""" return 1.0/wv.void_fraction*( self.base_transport.viscosity(cv, dv, eos)) def thermal_conductivity(self, cv: ConservedVars, dv: GasDependentVars, - wv: PorousWallVars, eos, wall_model) -> DOFArray: + wv: PorousWallVars, eos: GasEOS, + wall_model: PorousWallProperties) -> DOFArray: r"""Return the effective thermal conductivity of the gas+solid. It is a function of temperature and degradation progress. As the @@ -335,20 +344,20 @@ def thermal_conductivity(self, cv: ConservedVars, dv: GasDependentVars, return y_s*kappa_s + y_g*kappa_g def species_diffusivity(self, cv: ConservedVars, dv: GasDependentVars, - wv: PorousWallVars, eos) -> DOFArray: + wv: PorousWallVars, eos: GasEOS) -> DOFArray: """Mass diffusivity of gaseous species through the porous wall.""" return 1.0/wv.tortuosity*( self.base_transport.species_diffusivity(cv, dv, eos)) - # FIXME I have to pass "flow_model" but this class is internal to "flow_model".. - # Seems dumb to me but I dont know to access the other classes... def transport_vars(self, cv: ConservedVars, dv: GasDependentVars, - wv: PorousWallVars, eos, wall_model) -> GasTransportVars: + wv: PorousWallVars, eos: GasEOS, + wall_model: PorousWallProperties) -> GasTransportVars: r"""Compute the transport properties.""" return GasTransportVars( bulk_viscosity=self.bulk_viscosity(cv, dv, wv, eos), viscosity=self.viscosity(cv, dv, wv, eos), - thermal_conductivity=self.thermal_conductivity(cv, dv, wv, eos, wall_model), + thermal_conductivity=self.thermal_conductivity(cv, dv, wv, eos, + wall_model), species_diffusivity=self.species_diffusivity(cv, dv, wv, eos) ) @@ -371,8 +380,7 @@ class PorousFlowModel: .. attribute:: transport Transport class that governs how the gas flows through the porous - media. This is accounted for in - :class:`~mirgecom.transport.PorousWallTransport` + media. This is accounted for in :class:`PorousWallTransport` It also include functions that combine the properties of the porous material and the gas that permeates, yielding the actual porous flow EOS: @@ -385,7 +393,7 @@ class PorousFlowModel: .. automethod:: heat_capacity """ - eos: GasEOS + eos: MixtureEOS wall_model: PorousWallProperties transport: PorousWallTransport temperature_iteration = 3 @@ -466,7 +474,7 @@ def internal_energy(self, cv: ConservedVars, wv: PorousWallVars, \rho e = \epsilon_s \rho_s e_s + \epsilon_g \rho_g e_g """ return (cv.mass*self.eos.get_internal_energy(temperature, - cv.species_mass_fractions) + cv.species_mass_fractions) + wv.density*self.wall_model.enthalpy(temperature, wv.tau)) def heat_capacity(self, cv: ConservedVars, wv: PorousWallVars, From 8c9f9b3771b92efd7c06511b0eb00bdcbbfe9a5a Mon Sep 17 00:00:00 2001 From: Tulio Date: Fri, 18 Aug 2023 19:40:37 -0500 Subject: [PATCH 35/45] Ignore mypy error --- mirgecom/eos.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mirgecom/eos.py b/mirgecom/eos.py index fe183dd67..71bb6d39a 100644 --- a/mirgecom/eos.py +++ b/mirgecom/eos.py @@ -664,7 +664,8 @@ def gamma(self, cv: ConservedVars, temperature): # type: ignore[override] rspec = self.gas_const(cv) return cp / (cp - rspec) - def gas_const(self, cv: ConservedVars, temperature=None): + def gas_const(self, cv: ConservedVars, # type: ignore[override] + temperature=None): r"""Get specific gas constant $R_s$. The mixture specific gas constant is calculated From 166c261a0333860eab7d4753ff365c947ede56be Mon Sep 17 00:00:00 2001 From: Tulio Date: Sun, 20 Aug 2023 16:40:14 -0500 Subject: [PATCH 36/45] Cosmetic changes --- examples/ablation-workshop.py | 2 +- mirgecom/eos.py | 9 ++++----- mirgecom/gas_model.py | 8 +++----- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/examples/ablation-workshop.py b/examples/ablation-workshop.py index b86c33812..ea5d91326 100644 --- a/examples/ablation-workshop.py +++ b/examples/ablation-workshop.py @@ -475,7 +475,7 @@ def get_temperature_seed(self, cv: ConservedVars, def get_density(self, pressure, temperature, species_mass_fractions): raise NotImplementedError - def get_species_molecular_weights(self, temperature: DOFArray): + def get_species_molecular_weights(self): raise NotImplementedError def species_enthalpies(self, cv: ConservedVars, temperature: DOFArray): diff --git a/mirgecom/eos.py b/mirgecom/eos.py index 71bb6d39a..a63ebc452 100644 --- a/mirgecom/eos.py +++ b/mirgecom/eos.py @@ -44,12 +44,11 @@ """ from typing import Union, Optional from dataclasses import dataclass +from abc import ABCMeta, abstractmethod +from arraycontext import dataclass_array_container import numpy as np -from meshmode.mesh import BTAG_ALL, BTAG_NONE # noqa from meshmode.dof_array import DOFArray from mirgecom.fluid import ConservedVars, make_conserved -from abc import ABCMeta, abstractmethod -from arraycontext import dataclass_array_container class TemperatureSeedMissingError(Exception): @@ -237,7 +236,7 @@ def get_density(self, pressure, temperature, species_mass_fractions): """Get the density from pressure, temperature, and species fractions (Y).""" @abstractmethod - def get_species_molecular_weights(self, temperature: DOFArray): + def get_species_molecular_weights(self): """Get the species molecular weights.""" @abstractmethod @@ -783,7 +782,7 @@ def get_enthalpy(self, temperature, species_mass_fractions): return self._pyrometheus_mech.get_mixture_enthalpy_mass( temperature, species_mass_fractions) - def get_species_molecular_weights(self, temperature=None): + def get_species_molecular_weights(self): """Get the species molecular weights.""" return self._pyrometheus_mech.wts diff --git a/mirgecom/gas_model.py b/mirgecom/gas_model.py index 242826fcd..9b8b3061b 100644 --- a/mirgecom/gas_model.py +++ b/mirgecom/gas_model.py @@ -49,10 +49,10 @@ THE SOFTWARE. """ -import numpy as np # noqa from functools import partial from dataclasses import dataclass from typing import Optional +import numpy as np # noqa from arraycontext import dataclass_array_container from grudge.dof_desc import ( DD_VOLUME_ALL, @@ -432,10 +432,8 @@ def make_fluid_state(cv, gas_model, species_enthalpies=gas_model.eos.species_enthalpies(cv, temperature), ) - # FIXME I have to pass "gas_model" but this class is internal to "gas_model" - # Seems dumb to me but I dont know to access the other classes... - tv = gas_model.transport.transport_vars(cv, dv, wv, gas_model.eos, - gas_model.wall_model) + tv = gas_model.transport.transport_vars(cv=cv, dv=dv, wv=wv, + eos=gas_model.eos, wall_model=gas_model.wall_model) return PorousFlowFluidState(cv=cv, dv=dv, tv=tv, wv=wv) From 31c433c825df566b7db24cb218bbdf5fc26212d5 Mon Sep 17 00:00:00 2001 From: Tulio Date: Sun, 20 Aug 2023 16:57:44 -0500 Subject: [PATCH 37/45] Rename wall_model to wall_eos for consistency with gas nomenclature --- doc/mathematical/composite_material.rst | 8 +-- examples/ablation-workshop.py | 20 ++++---- mirgecom/gas_model.py | 12 ++--- mirgecom/materials/carbon_fiber.py | 6 +-- .../materials/prescribed_porous_material.py | 6 +-- mirgecom/materials/tacot.py | 6 +-- mirgecom/wall_model.py | 49 +++++++++---------- 7 files changed, 53 insertions(+), 54 deletions(-) diff --git a/doc/mathematical/composite_material.rst b/doc/mathematical/composite_material.rst index d35ab7c04..22539d13f 100644 --- a/doc/mathematical/composite_material.rst +++ b/doc/mathematical/composite_material.rst @@ -63,10 +63,10 @@ Carbon Fiber Oxidation :attr:`~mirgecom.eos.GasDependentVars.temperature` is evaluated using Newton iteration based on both :attr:`~mirgecom.eos.PyrometheusMixture.get_internal_energy` and - :attr:`~mirgecom.wall_model.PorousWallProperties.enthalpy`, + :attr:`~mirgecom.wall_model.PorousWallEOS.enthalpy`, as well as their respective derivatives, namely :attr:`~mirgecom.eos.PyrometheusMixture.heat_capacity_cv` and - :attr:`~mirgecom.wall_model.PorousWallProperties.heat_capacity`. + :attr:`~mirgecom.wall_model.PorousWallEOS.heat_capacity`. Note that :mod:`pyrometheus` is used to handle the species properties. Composite Materials @@ -141,10 +141,10 @@ Composite Materials :attr:`~mirgecom.eos.GasDependentVars.temperature`. It is evaluated using Newton iteration based on :attr:`~mirgecom.eos.PyrometheusMixture.get_internal_energy` and - :attr:`~mirgecom.wall_model.PorousWallProperties.enthalpy`, + :attr:`~mirgecom.wall_model.PorousWallEOS.enthalpy`, as well as their respective derivatives :attr:`~mirgecom.eos.PyrometheusMixture.heat_capacity_cv` and - :attr:`~mirgecom.wall_model.PorousWallProperties.heat_capacity`. + :attr:`~mirgecom.wall_model.PorousWallEOS.heat_capacity`. In *MIRGE-Com*, the solid properties are obtained by fitting polynomials to tabulated data for easy evaluation of the properties based on the diff --git a/examples/ablation-workshop.py b/examples/ablation-workshop.py index ea5d91326..a3fcc5b54 100644 --- a/examples/ablation-workshop.py +++ b/examples/ablation-workshop.py @@ -148,14 +148,14 @@ def initializer(dim, gas_model, material_densities, temperature, gas_const = gas_model.eos.gas_const(cv=None, temperature=temperature) if gas_density is None: - eps_gas = gas_model.wall_model.void_fraction(tau) + eps_gas = gas_model.wall_eos.void_fraction(tau) eps_rho_gas = eps_gas*pressure/(gas_const*temperature) # internal energy (kinetic energy is neglected) eps_rho_solid = sum(material_densities) bulk_energy = ( eps_rho_gas*gas_model.eos.get_internal_energy(temperature=temperature) - + eps_rho_solid*gas_model.wall_model.enthalpy(temperature, tau) + + eps_rho_solid*gas_model.wall_eos.enthalpy(temperature, tau) ) zeros = actx.np.zeros_like(tau) @@ -543,9 +543,9 @@ def get_temperature(self, cv, wv, tseed, niter=3): for _ in range(0, niter): eps_rho_e = (cv.mass*self.eos.get_internal_energy(temperature=temp) - + wv.density*self.wall_model.enthalpy(temp, wv.tau)) + + wv.density*self.wall_eos.enthalpy(temp, wv.tau)) bulk_cp = (cv.mass*self.eos.heat_capacity_cv(cv=cv, temperature=temp) - + wv.density*self.wall_model.heat_capacity(temp, wv.tau)) + + wv.density*self.wall_eos.heat_capacity(temp, wv.tau)) temp = temp - (eps_rho_e - cv.energy)/bulk_cp return temp @@ -683,7 +683,7 @@ def my_wall_bdry(u_minus): base_transport = GasTabulatedTransport() sample_transport = PorousWallTransport(base_transport=base_transport) - gas_model = PorousFlowModel(eos=my_gas, wall_model=my_material, + gas_model = PorousFlowModel(eos=my_gas, wall_eos=my_material, transport=sample_transport) # }}} @@ -737,10 +737,10 @@ def make_state(cv, temperature_seed, material_densities): material_densities=material_densities, tau=tau, density=gas_model.solid_density(material_densities), - void_fraction=gas_model.wall_model.void_fraction(tau), - emissivity=gas_model.wall_model.emissivity(tau), - permeability=gas_model.wall_model.permeability(tau), - tortuosity=gas_model.wall_model.tortuosity(tau) + void_fraction=gas_model.wall_eos.void_fraction(tau), + emissivity=gas_model.wall_eos.emissivity(tau), + permeability=gas_model.wall_eos.permeability(tau), + tortuosity=gas_model.wall_eos.tortuosity(tau) ) temperature = gas_model.get_temperature(cv=cv, wv=wv, @@ -759,7 +759,7 @@ def make_state(cv, temperature_seed, material_densities): ) tv = gas_model.transport.transport_vars( - cv=cv, dv=dv, wv=wv, eos=gas_model.eos, wall_model=gas_model.wall_model) + cv=cv, dv=dv, wv=wv, eos=gas_model.eos, wall_eos=gas_model.wall_eos) return PorousFlowFluidState(cv=cv, dv=dv, tv=tv, wv=wv) diff --git a/mirgecom/gas_model.py b/mirgecom/gas_model.py index 9b8b3061b..4791270a7 100644 --- a/mirgecom/gas_model.py +++ b/mirgecom/gas_model.py @@ -399,7 +399,7 @@ def make_fluid_state(cv, gas_model, elif isinstance(gas_model, PorousFlowModel): # FIXME per previous review, think of a way to de-couple wall and fluid. - # ~~~ we need to squeeze wall_model in gas_model because this is easily + # ~~~ we need to squeeze wall_eos in gas_model because this is easily # accessible everywhere in the code tau = gas_model.decomposition_progress(material_densities) @@ -407,10 +407,10 @@ def make_fluid_state(cv, gas_model, material_densities=material_densities, tau=tau, density=gas_model.solid_density(material_densities), - void_fraction=gas_model.wall_model.void_fraction(tau), - emissivity=gas_model.wall_model.emissivity(tau), - permeability=gas_model.wall_model.permeability(tau), - tortuosity=gas_model.wall_model.tortuosity(tau) + void_fraction=gas_model.wall_eos.void_fraction(tau), + emissivity=gas_model.wall_eos.emissivity(tau), + permeability=gas_model.wall_eos.permeability(tau), + tortuosity=gas_model.wall_eos.tortuosity(tau) ) temperature = gas_model.get_temperature(cv=cv, wv=wv, @@ -433,7 +433,7 @@ def make_fluid_state(cv, gas_model, ) tv = gas_model.transport.transport_vars(cv=cv, dv=dv, wv=wv, - eos=gas_model.eos, wall_model=gas_model.wall_model) + eos=gas_model.eos, wall_eos=gas_model.wall_eos) return PorousFlowFluidState(cv=cv, dv=dv, tv=tv, wv=wv) diff --git a/mirgecom/materials/carbon_fiber.py b/mirgecom/materials/carbon_fiber.py index c899476b6..5a614672f 100644 --- a/mirgecom/materials/carbon_fiber.py +++ b/mirgecom/materials/carbon_fiber.py @@ -1,7 +1,7 @@ r""":mod:`mirgecom.materials.carbon_fiber` evaluate carbon fiber data. .. autoclass:: Oxidation -.. autoclass:: FiberProperties +.. autoclass:: FiberEOS """ __copyright__ = """ @@ -32,7 +32,7 @@ from abc import abstractmethod import numpy as np from meshmode.dof_array import DOFArray -from mirgecom.wall_model import PorousWallProperties +from mirgecom.wall_model import PorousWallEOS class Oxidation: @@ -106,7 +106,7 @@ def get_source_terms(self, temperature, tau, rhoY_o2) -> DOFArray: # noqa N803 return (mw_co/mw_o2 + mw_o/mw_o2 - 1)*rhoY_o2*k*eff_surf_area -class FiberProperties(PorousWallProperties): +class FiberEOS(PorousWallEOS): """Evaluate the properties of the solid state containing only fibers. .. automethod:: void_fraction diff --git a/mirgecom/materials/prescribed_porous_material.py b/mirgecom/materials/prescribed_porous_material.py index 2d6079fd3..95e0d655e 100644 --- a/mirgecom/materials/prescribed_porous_material.py +++ b/mirgecom/materials/prescribed_porous_material.py @@ -2,7 +2,7 @@ The user can create any material with driver-oriented functions. -.. autoclass:: PrescribedProperties +.. autoclass:: PrescribedMaterialEOS """ __copyright__ = """ @@ -31,10 +31,10 @@ from typing import Optional from meshmode.dof_array import DOFArray -from mirgecom.wall_model import PorousWallProperties +from mirgecom.wall_model import PorousWallEOS -class PrescribedProperties(PorousWallProperties): +class PrescribedMaterialEOS(PorousWallEOS): """Evaluate the properties of a user-defined material. .. automethod:: void_fraction diff --git a/mirgecom/materials/tacot.py b/mirgecom/materials/tacot.py index 3b02c37b0..5b0f61804 100644 --- a/mirgecom/materials/tacot.py +++ b/mirgecom/materials/tacot.py @@ -5,7 +5,7 @@ Ablator) proposed as an open-source material for the Ablation Workshop. .. autoclass:: Pyrolysis -.. autoclass:: TACOTProperties +.. autoclass:: TacotEOS """ __copyright__ = """ @@ -35,7 +35,7 @@ import numpy as np from meshmode.dof_array import DOFArray from pytools.obj_array import make_obj_array -from mirgecom.wall_model import PorousWallProperties +from mirgecom.wall_model import PorousWallEOS # TODO generalize? define only abstract class and keep this in the driver? @@ -99,7 +99,7 @@ def get_source_terms(self, temperature, chi): actx.np.zeros_like(temperature)]) -class TACOTProperties(PorousWallProperties): +class TacotEOS(PorousWallEOS): """Evaluate the properties of the solid state containing resin and fibers. A linear weighting between the virgin and chared states is applied to diff --git a/mirgecom/wall_model.py b/mirgecom/wall_model.py index b958ec191..e405d8610 100644 --- a/mirgecom/wall_model.py +++ b/mirgecom/wall_model.py @@ -9,17 +9,17 @@ .. autoclass:: SolidWallDependentVars .. autoclass:: SolidWallState -Impermeable wall EOS --------------------- -.. autoclass:: SolidWallModel - -Material properties -------------------- +Impermeable material properties +------------------------------- The material properties are defined exclusively at the driver. The user must provide density, enthalpy, heat capacity and thermal conductivity of all constituents and their spatial distribution using :mod:`~mirgecom.utils.mask_from_elements`. +Impermeable wall model +---------------------- +.. autoclass:: SolidWallModel + Porous materials ^^^^^^^^^^^^^^^^ @@ -27,20 +27,20 @@ --------------------- .. autoclass:: PorousWallVars -Porous Media EOS ----------------- -.. autoclass:: PorousFlowModel - Porous Media Transport ---------------------- .. autoclass:: PorousWallTransport -Material properties -------------------- +Porous material properties +-------------------------- The properties of the materials are defined in specific files and used by - :class:`PorousWallProperties`. + :class:`PorousWallEOS`. + +.. autoclass:: PorousWallEOS -.. autoclass:: PorousWallProperties +Porous Media Model +------------------ +.. autoclass:: PorousFlowModel """ __copyright__ = """ @@ -221,7 +221,7 @@ class PorousWallVars: density: DOFArray -class PorousWallProperties: +class PorousWallEOS: """Abstract interface for porous media domains. The properties are material-dependent and specified in individual files @@ -323,8 +323,7 @@ def viscosity(self, cv: ConservedVars, dv: GasDependentVars, self.base_transport.viscosity(cv, dv, eos)) def thermal_conductivity(self, cv: ConservedVars, dv: GasDependentVars, - wv: PorousWallVars, eos: GasEOS, - wall_model: PorousWallProperties) -> DOFArray: + wv: PorousWallVars, eos: GasEOS, wall_eos: PorousWallEOS) -> DOFArray: r"""Return the effective thermal conductivity of the gas+solid. It is a function of temperature and degradation progress. As the @@ -338,7 +337,7 @@ def thermal_conductivity(self, cv: ConservedVars, dv: GasDependentVars, """ y_g = cv.mass/(cv.mass + wv.density) y_s = 1.0 - y_g - kappa_s = wall_model.thermal_conductivity(dv.temperature, wv.tau) + kappa_s = wall_eos.thermal_conductivity(dv.temperature, wv.tau) kappa_g = self.base_transport.thermal_conductivity(cv, dv, eos) return y_s*kappa_s + y_g*kappa_g @@ -351,13 +350,13 @@ def species_diffusivity(self, cv: ConservedVars, dv: GasDependentVars, def transport_vars(self, cv: ConservedVars, dv: GasDependentVars, wv: PorousWallVars, eos: GasEOS, - wall_model: PorousWallProperties) -> GasTransportVars: + wall_eos: PorousWallEOS) -> GasTransportVars: r"""Compute the transport properties.""" return GasTransportVars( bulk_viscosity=self.bulk_viscosity(cv, dv, wv, eos), viscosity=self.viscosity(cv, dv, wv, eos), thermal_conductivity=self.thermal_conductivity(cv, dv, wv, eos, - wall_model), + wall_eos), species_diffusivity=self.species_diffusivity(cv, dv, wv, eos) ) @@ -368,7 +367,7 @@ class PorousFlowModel: It is the equivalent to :class:`~mirgecom.gas_model.GasModel` and wraps: - .. attribute:: wall_model + .. attribute:: wall_eos The thermophysical properties of the wall material and its EOS. @@ -394,7 +393,7 @@ class PorousFlowModel: """ eos: MixtureEOS - wall_model: PorousWallProperties + wall_eos: PorousWallEOS transport: PorousWallTransport temperature_iteration = 3 @@ -419,7 +418,7 @@ def decomposition_progress(self, material_densities) -> DOFArray: $\tau=0$, then the fibers were all consumed. """ mass = self.solid_density(material_densities) - return self.wall_model.decomposition_progress(mass) + return self.wall_eos.decomposition_progress(mass) def get_temperature(self, cv: ConservedVars, wv: PorousWallVars, tseed: DOFArray, niter=temperature_iteration) -> DOFArray: @@ -475,7 +474,7 @@ def internal_energy(self, cv: ConservedVars, wv: PorousWallVars, """ return (cv.mass*self.eos.get_internal_energy(temperature, cv.species_mass_fractions) - + wv.density*self.wall_model.enthalpy(temperature, wv.tau)) + + wv.density*self.wall_eos.enthalpy(temperature, wv.tau)) def heat_capacity(self, cv: ConservedVars, wv: PorousWallVars, temperature: DOFArray) -> DOFArray: @@ -485,4 +484,4 @@ def heat_capacity(self, cv: ConservedVars, wv: PorousWallVars, \rho e = \epsilon_s \rho_s {C_p}_s + \epsilon_g \rho_g {C_v}_g """ return (cv.mass*self.eos.heat_capacity_cv(cv, temperature) - + wv.density*self.wall_model.heat_capacity(temperature, wv.tau)) + + wv.density*self.wall_eos.heat_capacity(temperature, wv.tau)) From 18b9daf71a0c133a4064aeeecebd7fc5fc3aced9 Mon Sep 17 00:00:00 2001 From: Tulio Date: Sun, 20 Aug 2023 17:02:41 -0500 Subject: [PATCH 38/45] Remove conflict thing --- mirgecom/multiphysics/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mirgecom/multiphysics/__init__.py b/mirgecom/multiphysics/__init__.py index 03792ba6c..471d876d8 100644 --- a/mirgecom/multiphysics/__init__.py +++ b/mirgecom/multiphysics/__init__.py @@ -28,7 +28,6 @@ .. automodule:: mirgecom.multiphysics.thermally_coupled_fluid_wall .. autofunction:: make_interface_boundaries ->>>>>>> main """ From 9052f638ab2f33c0d960ca3fa7f5a142a272340d Mon Sep 17 00:00:00 2001 From: Tulio Date: Sun, 20 Aug 2023 17:11:45 -0500 Subject: [PATCH 39/45] Fix AW example --- examples/ablation-workshop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ablation-workshop.py b/examples/ablation-workshop.py index a3fcc5b54..1b1afd61b 100644 --- a/examples/ablation-workshop.py +++ b/examples/ablation-workshop.py @@ -678,7 +678,7 @@ def my_wall_bdry(u_minus): my_gas = TabulatedGasEOS() bprime_class = BprimeTable() - my_material = my_composite.TACOTProperties(char_mass=220.0, virgin_mass=280.0) + my_material = my_composite.TacotEOS(char_mass=220.0, virgin_mass=280.0) pyrolysis = my_composite.Pyrolysis() base_transport = GasTabulatedTransport() From 4a74d2ff6e99f9ed9e7f0570cb0d78e3582c6d24 Mon Sep 17 00:00:00 2001 From: Tulio Date: Mon, 21 Aug 2023 10:13:59 -0500 Subject: [PATCH 40/45] Add mypy stuff to eos --- mirgecom/eos.py | 66 ++++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/mirgecom/eos.py b/mirgecom/eos.py index a63ebc452..149e19687 100644 --- a/mirgecom/eos.py +++ b/mirgecom/eos.py @@ -161,16 +161,17 @@ def kinetic_energy(self, cv: ConservedVars): """Get the kinetic energy for the gas.""" @abstractmethod - def gamma(self, cv: ConservedVars, temperature: DOFArray = None): + def gamma(self, cv: ConservedVars, temperature: Optional[DOFArray] = None): """Get the ratio of gas specific heats Cp/Cv.""" @abstractmethod - def get_density(self, pressure, temperature, species_mass_fractions=None): + def get_density(self, pressure, temperature, + species_mass_fractions: Optional[DOFArray] = None): """Get the density from pressure, and temperature.""" @abstractmethod def get_internal_energy(self, temperature: DOFArray, - species_mass_fractions: DOFArray = None) -> DOFArray: + species_mass_fractions: Optional[DOFArray] = None) -> DOFArray: """Get the fluid internal energy from temperature.""" def dependent_vars( @@ -314,11 +315,13 @@ def __init__(self, gamma=1.4, gas_const=287.1): self._gamma = gamma self._gas_const = gas_const - def gamma(self, cv: Optional[ConservedVars] = None, temperature=None): + def gamma(self, cv: Optional[ConservedVars] = None, + temperature: Optional[DOFArray] = None) -> DOFArray: """Get specific heat ratio Cp/Cv.""" return self._gamma - def heat_capacity_cp(self, cv: Optional[ConservedVars] = None, temperature=None): + def heat_capacity_cp(self, cv: Optional[ConservedVars] = None, + temperature: Optional[DOFArray] = None) -> DOFArray: r"""Get specific heat capacity at constant pressure. Parameters @@ -330,7 +333,8 @@ def heat_capacity_cp(self, cv: Optional[ConservedVars] = None, temperature=None) """ return self._gas_const * self._gamma / (self._gamma - 1) - def heat_capacity_cv(self, cv: Optional[ConservedVars] = None, temperature=None): + def heat_capacity_cv(self, cv: Optional[ConservedVars] = None, + temperature: Optional[DOFArray] = None) -> DOFArray: r"""Get specific heat capacity at constant volume. Parameters @@ -342,11 +346,13 @@ def heat_capacity_cv(self, cv: Optional[ConservedVars] = None, temperature=None) """ return self._gas_const / (self._gamma - 1) - def gas_const(self, cv: Optional[ConservedVars] = None, temperature=None): + def gas_const(self, cv: Optional[ConservedVars] = None, + temperature: Optional[DOFArray] = None) -> DOFArray: """Get specific gas constant R.""" return self._gas_const - def get_density(self, pressure, temperature, species_mass_fractions=None): + def get_density(self, pressure, temperature, + species_mass_fractions: Optional[DOFArray] = None) -> DOFArray: r"""Get gas density from pressure and temperature. The gas density is calculated as: @@ -402,7 +408,7 @@ def internal_energy(self, cv: ConservedVars): """ return (cv.energy - self.kinetic_energy(cv)) - def pressure(self, cv: ConservedVars, temperature=None): + def pressure(self, cv: ConservedVars, temperature: Optional[DOFArray] = None): r"""Get thermodynamic pressure of the gas. Gas pressure (p) is calculated from the internal energy (e) as: @@ -424,7 +430,7 @@ def pressure(self, cv: ConservedVars, temperature=None): """ return self.internal_energy(cv) * (self._gamma - 1.0) - def sound_speed(self, cv: ConservedVars, temperature=None): + def sound_speed(self, cv: ConservedVars, temperature: Optional[DOFArray] = None): r"""Get the speed of sound in the gas. The speed of sound (c) is calculated as: @@ -447,7 +453,8 @@ def sound_speed(self, cv: ConservedVars, temperature=None): actx = cv.array_context return actx.np.sqrt(self._gamma / cv.mass * self.pressure(cv)) - def temperature(self, cv: ConservedVars, temperature_seed: DOFArray = None): + def temperature(self, cv: ConservedVars, + temperature_seed: Optional[DOFArray] = None): r"""Get the thermodynamic temperature of the gas. The thermodynamic temperature (T) is calculated from @@ -477,7 +484,7 @@ def temperature(self, cv: ConservedVars, temperature_seed: DOFArray = None): * self.internal_energy(cv) / cv.mass) ) - def total_energy(self, cv, pressure, temperature=None): + def total_energy(self, cv, pressure, temperature: Optional[DOFArray] = None): r""" Get gas total energy from mass, pressure, and momentum. @@ -514,7 +521,8 @@ def total_energy(self, cv, pressure, temperature=None): return (pressure / (self._gamma - 1.0) + self.kinetic_energy(cv)) - def get_internal_energy(self, temperature, species_mass_fractions=None): + def get_internal_energy(self, temperature, + species_mass_fractions: Optional[DOFArray] = None): r"""Get the gas thermal energy from temperature. The gas internal energy $e$ is calculated from: @@ -596,7 +604,8 @@ def __init__(self, pyrometheus_mech, temperature_guess=300.0): self._pyrometheus_mech = pyrometheus_mech self._tguess = temperature_guess - def get_temperature_seed(self, cv, temperature_seed=None): + def get_temperature_seed(self, cv: ConservedVars, + temperature_seed: Optional[DOFArray] = None): """Get a *cv*-shaped array with which to seed temperature calcuation. Parameters @@ -618,7 +627,7 @@ def get_temperature_seed(self, cv, temperature_seed=None): tseed = temperature_seed return tseed if isinstance(tseed, DOFArray) else tseed * (0*cv.mass + 1.0) - def heat_capacity_cp(self, cv: ConservedVars, temperature): + def heat_capacity_cp(self, cv: ConservedVars, temperature: DOFArray) -> DOFArray: r"""Get mixture-averaged specific heat capacity at constant pressure. Parameters @@ -632,7 +641,7 @@ def heat_capacity_cp(self, cv: ConservedVars, temperature): return \ self._pyrometheus_mech.get_mixture_specific_heat_cp_mass(temperature, y) - def heat_capacity_cv(self, cv: ConservedVars, temperature): + def heat_capacity_cv(self, cv: ConservedVars, temperature: DOFArray) -> DOFArray: r"""Get mixture-averaged specific heat capacity at constant volume. Parameters @@ -648,7 +657,8 @@ def heat_capacity_cv(self, cv: ConservedVars, temperature): / self.gamma(cv, temperature) ) - def gamma(self, cv: ConservedVars, temperature): # type: ignore[override] + def gamma(self, cv: ConservedVars, # type: ignore[override] + temperature: DOFArray) -> DOFArray: r"""Get mixture-averaged heat capacity ratio, $\frac{C_p}{C_p - R_s}$. Parameters @@ -664,7 +674,7 @@ def gamma(self, cv: ConservedVars, temperature): # type: ignore[override] return cp / (cp - rspec) def gas_const(self, cv: ConservedVars, # type: ignore[override] - temperature=None): + temperature: Optional[DOFArray] = None) -> DOFArray: r"""Get specific gas constant $R_s$. The mixture specific gas constant is calculated @@ -744,7 +754,8 @@ def get_density(self, pressure, temperature, species_mass_fractions): return self._pyrometheus_mech.get_density(pressure, temperature, species_mass_fractions) - def get_internal_energy(self, temperature, species_mass_fractions): + def get_internal_energy(self, temperature, # type: ignore[override] + species_mass_fractions) -> DOFArray: r"""Get the gas thermal energy from temperature, and species fractions (Y). The gas internal energy $e$ is calculated from: @@ -763,7 +774,7 @@ def get_internal_energy(self, temperature, species_mass_fractions): return self._pyrometheus_mech.get_mixture_internal_energy_mass( temperature, species_mass_fractions) - def get_enthalpy(self, temperature, species_mass_fractions): + def get_enthalpy(self, temperature, species_mass_fractions) -> DOFArray: r"""Get the gas enthalpy from temperature, and species fractions (Y). The enthalpy of the gas mixture $h$ is calculated from: @@ -786,13 +797,14 @@ def get_species_molecular_weights(self): """Get the species molecular weights.""" return self._pyrometheus_mech.wts - def species_enthalpies(self, cv: ConservedVars, temperature): + def species_enthalpies(self, cv: ConservedVars, + temperature: DOFArray) -> DOFArray: """Get the species specific enthalpies.""" spec_r = self._pyrometheus_mech.gas_constant/self._pyrometheus_mech.wts return (spec_r * temperature * self._pyrometheus_mech.get_species_enthalpies_rt(temperature)) - def get_production_rates(self, cv: ConservedVars, temperature): + def get_production_rates(self, cv: ConservedVars, temperature: DOFArray): r"""Get the production rate for each species. Parameters @@ -835,7 +847,7 @@ def pressure(self, cv: ConservedVars, temperature): y = cv.species_mass_fractions return self._pyrometheus_mech.get_pressure(cv.mass, temperature, y) - def sound_speed(self, cv: ConservedVars, temperature): + def sound_speed(self, cv: ConservedVars, temperature: DOFArray): r"""Get the speed of sound in the gas. The speed of sound ($c$) is calculated as: @@ -861,7 +873,8 @@ def sound_speed(self, cv: ConservedVars, temperature): * self.pressure(cv, temperature)) / cv.mass) - def temperature(self, cv: ConservedVars, temperature_seed=None): + def temperature(self, cv: ConservedVars, + temperature_seed: Optional[DOFArray] = None): r"""Get the thermodynamic temperature of the gas. The thermodynamic temperature ($T$) is calculated from @@ -897,7 +910,8 @@ def temperature(self, cv: ConservedVars, temperature_seed=None): e = self.internal_energy(cv) / cv.mass return self._pyrometheus_mech.get_temperature(e, tseed, y) - def total_energy(self, cv, pressure, temperature): + def total_energy(self, cv: ConservedVars, pressure: DOFArray, + temperature: DOFArray): r""" Get gas total energy from mass, pressure, and momentum. @@ -936,7 +950,7 @@ def total_energy(self, cv, pressure, temperature): return (pressure / (self.gamma(cv, temperature) - 1.0) + self.kinetic_energy(cv)) - def get_species_source_terms(self, cv: ConservedVars, temperature): + def get_species_source_terms(self, cv: ConservedVars, temperature: DOFArray): r"""Get the species mass source terms to be used on the RHS for chemistry. Parameters From b06eb909aa6c06dbba7e162fce2140425af485cc Mon Sep 17 00:00:00 2001 From: Tulio Date: Mon, 21 Aug 2023 11:36:05 -0500 Subject: [PATCH 41/45] Fix missing 'gas_model.wall_model' left --- mirgecom/materials/initializer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mirgecom/materials/initializer.py b/mirgecom/materials/initializer.py index e9dac2f88..9222e3e33 100644 --- a/mirgecom/materials/initializer.py +++ b/mirgecom/materials/initializer.py @@ -86,7 +86,7 @@ def __call__(self, dim, x_vec, gas_model): tau = gas_model.decomposition_progress(wall_density) - eps_gas = gas_model.wall_model.void_fraction(tau) + eps_gas = gas_model.wall_eos.void_fraction(tau) if self._mass is None: pressure = self._pres + zeros eps_rho_gas = eps_gas*gas_model.eos.get_density(pressure, @@ -98,7 +98,7 @@ def __call__(self, dim, x_vec, gas_model): # internal energy (kinetic energy is neglected) eps_rho_solid = sum(wall_density) bulk_energy = ( - eps_rho_solid*gas_model.wall_model.enthalpy(temperature, tau) + eps_rho_solid*gas_model.wall_eos.enthalpy(temperature, tau) + eps_rho_gas*gas_model.eos.get_internal_energy(temperature, species_mass_frac) ) From ce8d26e6035f1c69ebae7d4f0ba141246835714d Mon Sep 17 00:00:00 2001 From: Tulio Date: Mon, 21 Aug 2023 14:00:02 -0500 Subject: [PATCH 42/45] Fix porous wall initializer --- mirgecom/materials/initializer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mirgecom/materials/initializer.py b/mirgecom/materials/initializer.py index 9222e3e33..b0977bdce 100644 --- a/mirgecom/materials/initializer.py +++ b/mirgecom/materials/initializer.py @@ -96,7 +96,8 @@ def __call__(self, dim, x_vec, gas_model): eps_rho_gas = eps_gas*density # internal energy (kinetic energy is neglected) - eps_rho_solid = sum(wall_density) + eps_rho_solid = gas_model.solid_density(wall_density) + bulk_energy = ( eps_rho_solid*gas_model.wall_eos.enthalpy(temperature, tau) + eps_rho_gas*gas_model.eos.get_internal_energy(temperature, From 071e652a4696583e2b4c59606c276539b3bf1131 Mon Sep 17 00:00:00 2001 From: Tulio Date: Mon, 21 Aug 2023 14:01:59 -0500 Subject: [PATCH 43/45] Ignore change to transport --- mirgecom/transport.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mirgecom/transport.py b/mirgecom/transport.py index 7707c3e90..4f6e5bf6f 100644 --- a/mirgecom/transport.py +++ b/mirgecom/transport.py @@ -50,6 +50,7 @@ from dataclasses import dataclass from arraycontext import dataclass_array_container import numpy as np +from meshmode.mesh import BTAG_ALL, BTAG_NONE # noqa from meshmode.dof_array import DOFArray from mirgecom.fluid import ConservedVars from mirgecom.eos import GasEOS, GasDependentVars From 60e6864d450df6d8ddc2a5ed401ddd64e590dc1f Mon Sep 17 00:00:00 2001 From: Tulio Date: Mon, 21 Aug 2023 18:19:46 -0500 Subject: [PATCH 44/45] Matts review --- doc/mathematical/composite_material.rst | 2 +- examples/ablation-workshop.py | 11 ++--------- mirgecom/wall_model.py | 10 +++++----- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/doc/mathematical/composite_material.rst b/doc/mathematical/composite_material.rst index 22539d13f..cc3a1a895 100644 --- a/doc/mathematical/composite_material.rst +++ b/doc/mathematical/composite_material.rst @@ -13,7 +13,7 @@ Carbon Fiber Oxidation The temporal evolution of mass density is solved in order to predict the material degradation. As the - :attr:`~mirgecom.materials.carbon_fiber.Oxidation` progresses, + :class:`~mirgecom.materials.carbon_fiber.Oxidation` progresses, the temporal evolution of the fibers mass is given by .. math :: diff --git a/examples/ablation-workshop.py b/examples/ablation-workshop.py index 1b1afd61b..593ea6f31 100644 --- a/examples/ablation-workshop.py +++ b/examples/ablation-workshop.py @@ -67,6 +67,7 @@ MixtureDependentVars, MixtureEOS ) +from mirgecom.transport import TransportModel from mirgecom.gas_model import PorousFlowFluidState from mirgecom.wall_model import ( PorousWallVars, @@ -247,7 +248,7 @@ def __init__(self): scipy.interpolate.CubicSpline(self.T_bounds, self.Hw[i, :]).c -class GasTabulatedTransport: +class GasTabulatedTransport(TransportModel): """Evaluate tabulated transport data for TACOT.""" def __init__(self, prandtl=1.0, lewis=1.0): @@ -342,14 +343,6 @@ class TabulatedGasEOS(MixtureEOS): The table was extracted from the suplementar material from the ablation workshop. Some lines were removed to reduce the number of spline interpolation segments. - - .. automethod:: molar_mass - .. automethod:: enthalpy - .. automethod:: gamma - .. automethod:: get_internal_energy - .. automethod:: heat_capacity_cp - .. automethod:: heat_capacity_cv - .. automethod:: pressure """ def __init__(self): diff --git a/mirgecom/wall_model.py b/mirgecom/wall_model.py index e405d8610..ed46c0730 100644 --- a/mirgecom/wall_model.py +++ b/mirgecom/wall_model.py @@ -84,7 +84,7 @@ MixtureEOS, GasDependentVars ) -from mirgecom.transport import GasTransportVars +from mirgecom.transport import GasTransportVars, TransportModel @with_container_arithmetic(bcast_obj_array=False, @@ -301,7 +301,7 @@ class PorousWallTransport: .. automethod:: species_diffusivity """ - def __init__(self, base_transport): + def __init__(self, base_transport: TransportModel): """Initialize transport model.""" self.base_transport = base_transport @@ -395,7 +395,7 @@ class PorousFlowModel: eos: MixtureEOS wall_eos: PorousWallEOS transport: PorousWallTransport - temperature_iteration = 3 + temperature_iteration: int = 3 def solid_density(self, material_densities) -> DOFArray: r"""Return the solid density $\epsilon_s \rho_s$. @@ -421,7 +421,7 @@ def decomposition_progress(self, material_densities) -> DOFArray: return self.wall_eos.decomposition_progress(mass) def get_temperature(self, cv: ConservedVars, wv: PorousWallVars, - tseed: DOFArray, niter=temperature_iteration) -> DOFArray: + tseed: DOFArray) -> DOFArray: r"""Evaluate the temperature based on solid+gas properties. It uses the assumption of thermal equilibrium between solid and fluid. @@ -443,7 +443,7 @@ def get_temperature(self, cv: ConservedVars, wv: PorousWallVars, internal_energy = cv.energy - 0.5/cv.mass*np.dot(cv.momentum, cv.momentum) - for _ in range(0, niter): + for _ in range(0, self.temperature_iteration): eps_rho_e = self.internal_energy(cv, wv, temp) bulk_cp = self.heat_capacity(cv, wv, temp) temp = temp - (eps_rho_e - internal_energy)/bulk_cp From 378c6224ae48768e3d23145c8bff7f8915b8f8ef Mon Sep 17 00:00:00 2001 From: Tulio Date: Mon, 21 Aug 2023 18:29:28 -0500 Subject: [PATCH 45/45] mypy --- examples/ablation-workshop.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/examples/ablation-workshop.py b/examples/ablation-workshop.py index 593ea6f31..3e8d46920 100644 --- a/examples/ablation-workshop.py +++ b/examples/ablation-workshop.py @@ -65,7 +65,9 @@ ) from mirgecom.eos import ( MixtureDependentVars, - MixtureEOS + MixtureEOS, + GasEOS, + GasDependentVars ) from mirgecom.transport import TransportModel from mirgecom.gas_model import PorousFlowFluidState @@ -303,22 +305,26 @@ def __init__(self, prandtl=1.0, lewis=1.0): self._cs_viscosity = CubicSpline(gas_data[:, 0], gas_data[:, 1]*1e-4) - def bulk_viscosity(self, cv, dv, eos) -> DOFArray: + def bulk_viscosity(self, cv: ConservedVars, # type: ignore[override] + dv: GasDependentVars, eos: GasEOS) -> DOFArray: r"""Get the bulk viscosity for the gas, $\mu_{B}$.""" actx = cv.mass.array_context return actx.np.zeros_like(cv.mass) - def volume_viscosity(self, cv, dv, eos) -> DOFArray: + def volume_viscosity(self, cv: ConservedVars, # type: ignore[override] + dv: GasDependentVars, eos: GasEOS) -> DOFArray: r"""Get the 2nd viscosity coefficent, $\lambda$.""" return (self.bulk_viscosity(cv, dv, eos) - 2./3.)*self.viscosity(cv, dv, eos) - def viscosity(self, cv, dv, eos) -> DOFArray: + def viscosity(self, cv: ConservedVars, # type: ignore[override] + dv: GasDependentVars, eos: GasEOS) -> DOFArray: r"""Return the gas viscosity $\mu$.""" coeffs = self._cs_viscosity.c bnds = self._cs_viscosity.x return eval_spline(dv.temperature, bnds, coeffs) - def thermal_conductivity(self, cv, dv, eos) -> DOFArray: + def thermal_conductivity(self, cv: ConservedVars, # type: ignore[override] + dv: GasDependentVars, eos: GasEOS) -> DOFArray: r"""Return the gas thermal conductivity $\kappa_g$. .. math:: @@ -331,7 +337,8 @@ def thermal_conductivity(self, cv, dv, eos) -> DOFArray: mu = self.viscosity(cv, dv, eos) return mu*cp/self._prandtl - def species_diffusivity(self, cv, dv, eos) -> DOFArray: + def species_diffusivity(self, cv: ConservedVars, # type: ignore[override] + dv: GasDependentVars, eos: GasEOS) -> DOFArray: return cv.species_mass # empty array