diff --git a/Documentation/ProjectFile/material/fluid/viscosity/t_type.md b/Documentation/ProjectFile/material/fluid/viscosity/t_type.md index 713d4d928b9ca1c320bc43cb5ad114d0b58834e0..26670a401a5c104a644801133f67709093f69f42 100644 --- a/Documentation/ProjectFile/material/fluid/viscosity/t_type.md +++ b/Documentation/ProjectFile/material/fluid/viscosity/t_type.md @@ -1,2 +1,2 @@ Type of viscosity model. It can be Constant, LinearPressure, TemperatureDependent, - or Vogels. + Vogels, or WaterViscosityIAPWS diff --git a/MaterialLib/Fluid/FluidProperty.h b/MaterialLib/Fluid/FluidProperty.h index fce5a89dc3e900b64e456cd28cc042e618a3bb90..cc3d30876847b6c16756cbacee6e5c86ad27a797 100644 --- a/MaterialLib/Fluid/FluidProperty.h +++ b/MaterialLib/Fluid/FluidProperty.h @@ -25,6 +25,7 @@ enum class PropertyVariableType { T = 0, ///< temperature. p = 1, ///< pressure. + rho = p, ///< density. For some models, rho substitutes p number_of_variables = 2 ///< Number of property variables. }; diff --git a/MaterialLib/Fluid/Viscosity/WaterViscosityIAPWS.cpp b/MaterialLib/Fluid/Viscosity/WaterViscosityIAPWS.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c6e1e885e039ca9236cf5ac84424aaeb27b0577c --- /dev/null +++ b/MaterialLib/Fluid/Viscosity/WaterViscosityIAPWS.cpp @@ -0,0 +1,203 @@ +/** + * \copyright + * Copyright (c) 2012-2016, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + * \file WaterViscosityIAPWS.cpp + * + * Created on December 1, 2016, 1:41 PM + */ + +#include "WaterViscosityIAPWS.h" + +#include <array> +#include <cmath> + +namespace MaterialLib +{ +namespace Fluid +{ +static const double Hi[4] = {1.67752, 2.20462, 0.6366564, -0.241605}; +static const double Hij[6][7] = { + {0.520094, 0.222531, -0.281378, 0.161913, -0.0325372, 0, 0}, + {0.0850895, 0.999115, -0.906851, 0.257399, 0, 0, 0}, + {-1.08374, 1.88797, -0.772479, 0, 0, 0, 0}, + {-0.289555, 1.26613, -0.489837, 0, 0.0698452, 0, -0.00435673}, + {0, 0, -0.25704, 0, 0, 0.00872102, 0}, + {0, 0.120573, 0, 0, 0, 0, -0.000593264}}; + +static double computeBarMu0Factor(const double barT); + +static std::array<double, 6> computeSeriesFactorTForMu1(const double barT, + const double bar_rho); +static std::array<double, 7> computeSeriesFactorRhoForMu1(const double barT, + const double bar_rho); +static double computeBarMu1Factor( + const std::array<double, 6>& series_factorT, + const std::array<double, 7>& series_factorRho); + +static double computedBarMu_dbarT(const double barT, double bar_rho); +static double computedBarMu_dbarRho(const double barT, double bar_rho); + +double WaterViscosityIAPWS::getValue(const ArrayType& var_vals) const +{ + const double bar_T = + var_vals[static_cast<unsigned>(PropertyVariableType::T)] / _ref_T; + const double bar_rho = + var_vals[static_cast<unsigned>(PropertyVariableType::rho)] / _ref_rho; + + const double mu0 = 100. * std::sqrt(bar_T) / computeBarMu0Factor(bar_T); + + const auto& series_factorT = computeSeriesFactorTForMu1(bar_T, bar_rho); + const auto& series_factorRho = computeSeriesFactorRhoForMu1(bar_T, bar_rho); + const double mu1 = std::exp( + bar_rho * computeBarMu1Factor(series_factorT, series_factorRho)); + + return mu0 * mu1 * _ref_mu; +} + +double WaterViscosityIAPWS::getdValue(const ArrayType& var_vals, + const PropertyVariableType var_type) const +{ + const double bar_T = + var_vals[static_cast<unsigned>(PropertyVariableType::T)] / _ref_T; + const double bar_rho = + var_vals[static_cast<unsigned>(PropertyVariableType::rho)] / _ref_rho; + + switch (var_type) + { + case PropertyVariableType::T: + return _ref_mu * computedBarMu_dbarT(bar_T, bar_rho) / _ref_T; + case PropertyVariableType::rho: + return _ref_mu * computedBarMu_dbarRho(bar_T, bar_rho) / _ref_rho; + default: + return 0.; + } +} + +double computeBarMu0Factor(const double barT) +{ + double sum_val = 0.; + double barT_i = 1.; + for (int i = 0; i < 4; i++) + { + sum_val += (Hi[i] / barT_i); + barT_i *= barT; + } + return sum_val; +} + +std::array<double, 6> computeSeriesFactorTForMu1(const double barT, + const double bar_rho) +{ + std::array<double, 6> series_factorT; + series_factorT[0] = 1.; + const double barT_fac = 1 / barT - 1.0; + for (int i = 1; i < 6; i++) + { + series_factorT[i] = series_factorT[i - 1] * barT_fac; + } + + return series_factorT; +} + +std::array<double, 7> computeSeriesFactorRhoForMu1(const double barT, + const double bar_rho) +{ + std::array<double, 7> series_factorRho; + series_factorRho[0] = 1.; + for (int i = 1; i < 7; i++) + { + series_factorRho[i] = series_factorRho[i - 1] * (bar_rho - 1.0); + } + + return series_factorRho; +} + +double computeBarMu1Factor(const std::array<double, 6>& series_factorT, + const std::array<double, 7>& series_factorRho) +{ + double sum_val = 0.; + for (int i = 0; i < 6; i++) + { + double sum_val_j = 0; + for (int j = 0; j < 7; j++) + { + sum_val_j += Hij[i][j] * series_factorRho[j]; + } + sum_val += series_factorT[i] * sum_val_j; + } + + return sum_val; +} + +double computedBarMu_dbarT(const double barT, double bar_rho) +{ + const double mu0_factor = computeBarMu0Factor(barT); + const double sqrt_barT = std::sqrt(barT); + + double dmu0_factor_dbarT = 0.0; + double barT_i = barT * barT; + for (int i = 1; i < 4; i++) + { + dmu0_factor_dbarT -= static_cast<double>(i) * (Hi[i] / barT_i); + barT_i *= barT; + } + + const double dbar_mu0_dbarT = + 50. / (mu0_factor * sqrt_barT) - + 100. * sqrt_barT * dmu0_factor_dbarT / (mu0_factor * mu0_factor); + + const auto& series_factorT = computeSeriesFactorTForMu1(barT, bar_rho); + const auto& series_factorRho = computeSeriesFactorRhoForMu1(barT, bar_rho); + + double dmu1_factor_dbarT = 0.0; + for (int i = 1; i < 6; i++) + { + double sum_val_j = 0; + for (int j = 0; j < 7; j++) + { + sum_val_j += Hij[i][j] * series_factorRho[j]; + } + dmu1_factor_dbarT -= static_cast<double>(i) * series_factorT[i - 1] * + sum_val_j / (barT * barT); + } + + const double mu1_factor = + computeBarMu1Factor(series_factorT, series_factorRho); + const double dbar_mu1_dbarT = + bar_rho * std::exp(bar_rho * mu1_factor) * dmu1_factor_dbarT; + + return dbar_mu0_dbarT * std::exp(bar_rho * mu1_factor) + + dbar_mu1_dbarT * 100. * sqrt_barT / mu0_factor; +} + +double computedBarMu_dbarRho(const double barT, double bar_rho) +{ + const auto& series_factorT = computeSeriesFactorTForMu1(barT, bar_rho); + const auto& series_factorRho = computeSeriesFactorRhoForMu1(barT, bar_rho); + + double dmu1_factor_dbar_rho = 0.0; + for (int i = 0; i < 6; i++) + { + double sum_val_j = 0; + for (int j = 1; j < 7; j++) + { + sum_val_j += + static_cast<double>(j) * Hij[i][j] * series_factorRho[j - 1]; + } + dmu1_factor_dbar_rho += series_factorT[i] * sum_val_j; + } + + const double mu0 = 100. * std::sqrt(barT) / computeBarMu0Factor(barT); + + const double mu1_factor = + computeBarMu1Factor(series_factorT, series_factorRho); + return mu0 * std::exp(bar_rho * mu1_factor) * + (mu1_factor + bar_rho * dmu1_factor_dbar_rho); +} + +} // end namespace +} // end namespace diff --git a/MaterialLib/Fluid/Viscosity/WaterViscosityIAPWS.h b/MaterialLib/Fluid/Viscosity/WaterViscosityIAPWS.h new file mode 100644 index 0000000000000000000000000000000000000000..2411f9f8762d9868dc844776e258d37a4da11983 --- /dev/null +++ b/MaterialLib/Fluid/Viscosity/WaterViscosityIAPWS.h @@ -0,0 +1,89 @@ +/** + * \brief Viscosity model according to + * Release on the IAPWS Formulation 2008 for the Viscosity of Ordinary + * Water Substance + * + * \copyright + * Copyright (c) 2012-2016, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + * \file WaterViscosityIAPWS.h + * + * Created on December 1, 2016, 1:41 PM + */ + +#ifndef OGS_WATER_VISCOSITY_IAPWS_H +#define OGS_WATER_VISCOSITY_IAPWS_H + +#include <string> + +#include "MaterialLib/Fluid/FluidProperty.h" + +namespace MaterialLib +{ +namespace Fluid +{ +/** + * \brief A class for viscosity model that is defined by + * The International Association for the Properties of Water and Steam + * <a href="http://www.iapws.org/relguide/visc.pdf">IAPWS</a> + * + * With the definition, the viscosity is a function of temperature and + * water density + * + * \attention The critical enhancement, \f$\bar{\mu}_2\f$, which is significant + * only within the boundaries specified by + * \f[ T (\mbox{in K}) \in (645.91, 650.77) \f] + * and + * \f[ \rho (\mbox{in kg m}^{-3}) \in (245.8, 405.3)\f], + * is not considered. + */ +class WaterViscosityIAPWS final : public FluidProperty +{ +public: + WaterViscosityIAPWS() = default; + + /// Get model name. + std::string getName() const override + { + return "IAPWS temperature-density dependent viscosity model"; + } + + /** + * Get density value. + * \param var_vals Variable values of temperature and water density in + * an array. The order of its elements + * is given in enum class PropertyVariableType. + * + * \attention The variable values must follow the SI standard, + * e.g temperature unit is K. + */ + double getValue(const ArrayType& var_vals) const override; + + /** Get the partial differential of the density with respect to temperature + * or liquid pressure. + * \param var_vals Variable values of temperature and water density in + * an array. The order of its elements + * is given in enum class PropertyVariableType. + * + * \attention The variable values must follow the SI standard, + * e.g temperature unit is K. + * \param var_type Variable type. + */ + double getdValue(const ArrayType& var_vals, + const PropertyVariableType var_type) const override; + +private: + const double _ref_T = 647.096; ///< reference temperature in K + const double _ref_rho = 322.0; ///< reference density in `kg/m^3` + const double _ref_mu = 1.0e-6; ///< reference viscosity in Pa.s + + // Coefficients Hi and Hij are given in two static arrays in the cpp file. +}; + +} // end namespace +} // end namespace + +#endif /* OGS_WATER_VISCOSITY_IAPWS_H */ diff --git a/MaterialLib/Fluid/Viscosity/createViscosityModel.cpp b/MaterialLib/Fluid/Viscosity/createViscosityModel.cpp index 7f70b56b7c4d910536a4cb6da330f7e4b67932f4..740912ef2c48b7b177eea4a1d4033e4db7831159 100644 --- a/MaterialLib/Fluid/Viscosity/createViscosityModel.cpp +++ b/MaterialLib/Fluid/Viscosity/createViscosityModel.cpp @@ -19,6 +19,7 @@ #include "LinearPressureDependentViscosity.h" #include "TemperatureDependentViscosity.h" #include "VogelsLiquidDynamicViscosity.h" +#include "WaterViscosityIAPWS.h" namespace MaterialLib { @@ -135,6 +136,12 @@ std::unique_ptr<FluidProperty> createViscosityModel( fluid_type.data()); } } + else if (type == "WaterViscosityIAPWS") + { + //! \ogs_file_param{material__fluid__viscosity__type} + config.checkConfigParameter("type", "WaterViscosityIAPWS"); + return std::unique_ptr<FluidProperty>(new WaterViscosityIAPWS()); + } else { OGS_FATAL( diff --git a/Tests/MaterialLib/TestFluidViscosity.cpp b/Tests/MaterialLib/TestFluidViscosity.cpp index 6be91542b0ed56b54fc33d9285085c56b94eb04b..8cb6686322df087665120196c70343384ab6bbd0 100644 --- a/Tests/MaterialLib/TestFluidViscosity.cpp +++ b/Tests/MaterialLib/TestFluidViscosity.cpp @@ -45,8 +45,8 @@ TEST(Material, checkConstantViscosity) ArrayType dummy; ASSERT_EQ(1.e-4, mu->getValue(dummy)); - ASSERT_EQ(0.0, - mu->getdValue(dummy, MaterialLib::Fluid::PropertyVariableType::T)); + ASSERT_EQ( + 0.0, mu->getdValue(dummy, MaterialLib::Fluid::PropertyVariableType::T)); } TEST(Material, checkTemperatureDependentViscosity) @@ -64,9 +64,10 @@ TEST(Material, checkTemperatureDependentViscosity) vars[0] = 350.0; const double mu_expected = 1.e-3 * std::exp(-(vars[0] - 293) / 368); ASSERT_NEAR(mu_expected, mu->getValue(vars), 1.e-10); - ASSERT_NEAR(-mu_expected, - mu->getdValue(vars, MaterialLib::Fluid::PropertyVariableType::T), - 1.e-10); + ASSERT_NEAR( + -mu_expected, + mu->getdValue(vars, MaterialLib::Fluid::PropertyVariableType::T), + 1.e-10); } TEST(Material, checkLinearPressureDependentViscosity) @@ -85,9 +86,10 @@ TEST(Material, checkLinearPressureDependentViscosity) vars[1] = 2.e+6; ASSERT_NEAR(1.e-3 * (1. + 1.e-6 * (vars[1] - 1.e+5)), mu->getValue(vars), 1.e-10); - ASSERT_NEAR(1.e-9, - mu->getdValue(vars, MaterialLib::Fluid::PropertyVariableType::p), - 1.e-10); + ASSERT_NEAR( + 1.e-9, + mu->getdValue(vars, MaterialLib::Fluid::PropertyVariableType::p), + 1.e-10); } TEST(Material, checkVogelViscosity) @@ -124,3 +126,50 @@ TEST(Material, checkVogelViscosity) ASSERT_NEAR(0.352072e-4, mu_ch4->getValue(vars), 1.e-5); ASSERT_NEAR(-2.35664e-6, mu_ch4->getdValue(vars, var_type), 1.e-5); } + +TEST(Material, checkWaterViscosityIAPWS) +{ + const char xml_w[] = + "<viscosity>" + " <type>WaterViscosityIAPWS</type>" + "</viscosity>"; + + // Test data provided on http://www.iapws.org/relguide/visc.pdf + const auto mu_w = createTestViscosityModel(xml_w); + const double T[] = {298.15, 298.15, 373.15, 433.15, 433.15, 873.15, + 873.15, 873.15, 1173.15, 1173.15, 1173.15}; + const double rho[] = {998, 1200, 1000, 1, 1000, 1, 100, 600, 1, 100, 400}; + + const double expected_mumu[] = { + 889.735100, 1437.649467, 307.883622, 14.538324, 217.685358, 32.619287, + 35.802262, 77.430195, 44.217245, 47.640433, 64.154608}; + + const double perturbation = 1.e-9; + ArrayType vars; + for (int i = 0; i < 11; i++) + { + // Test mu + vars[static_cast<unsigned>(PropertyVariableType::T)] = T[i]; + vars[static_cast<unsigned>(PropertyVariableType::rho)] = rho[i]; + const double mu = mu_w->getValue(vars); + ASSERT_NEAR(expected_mumu[i] * 1.e-6, mu, 1.e-9); + + const double dmu_dT = mu_w->getdValue(vars, PropertyVariableType::T); + const double dmu_drho = + mu_w->getdValue(vars, PropertyVariableType::rho); + + // Test dmu/dT + vars[static_cast<unsigned>(PropertyVariableType::T)] = + T[i] + perturbation; + double mu1 = mu_w->getValue(vars); + ASSERT_NEAR((mu1 - mu) / perturbation, dmu_dT, 1.e-7); + + // Test dmu/drho + vars[static_cast<unsigned>(PropertyVariableType::T)] = T[i]; + vars[static_cast<unsigned>(PropertyVariableType::rho)] = + rho[i] + perturbation; + mu1 = mu_w->getValue(vars); + + ASSERT_NEAR((mu1 - mu) / perturbation, dmu_drho, 1.e-7); + } +}