diff --git a/docs/source/pythonapi/model.rst b/docs/source/pythonapi/model.rst index 6aff6d4c2..9ed77f366 100644 --- a/docs/source/pythonapi/model.rst +++ b/docs/source/pythonapi/model.rst @@ -10,9 +10,9 @@ Convenience Functions :nosignatures: :template: myfunction.rst + openmc.model.borated_water openmc.model.get_hexagonal_prism openmc.model.get_rectangular_prism - openmc.model.make_borated_water openmc.model.subdivide TRISO Fuel Modeling diff --git a/openmc/data/data.py b/openmc/data/data.py index 45a4406db..a7c0e536f 100644 --- a/openmc/data/data.py +++ b/openmc/data/data.py @@ -251,19 +251,19 @@ def water_density(temperature, pressure=0.1013): warn("Results are not valid for temperatures above 623.15 K.") # IAPWS region 4 parameters - _n4 = [0.11670521452767e4, -0.72421316703206e6, -0.17073846940092e2, - 0.12020824702470e5, -0.32325550322333e7, 0.14915108613530e2, - -0.48232657361591e4, 0.40511340542057e6, -0.23855557567849, - 0.65017534844798e3] + n4 = [0.11670521452767e4, -0.72421316703206e6, -0.17073846940092e2, + 0.12020824702470e5, -0.32325550322333e7, 0.14915108613530e2, + -0.48232657361591e4, 0.40511340542057e6, -0.23855557567849, + 0.65017534844798e3] # Compute the saturation temperature at the given pressure. beta = pressure**(0.25) - E = beta**2 + _n4[2] * beta + _n4[5] - F = _n4[0] * beta**2 + _n4[3] * beta + _n4[6] - G = _n4[1] * beta**2 + _n4[4] * beta + _n4[7] + E = beta**2 + n4[2] * beta + n4[5] + F = n4[0] * beta**2 + n4[3] * beta + n4[6] + G = n4[1] * beta**2 + n4[4] * beta + n4[7] D = 2.0 * G / (-F - sqrt(F**2 - 4 * E * G)) - T_sat = 0.5 * (_n4[9] + D - - sqrt((_n4[9] + D)**2 - 4.0 * (_n4[8] + _n4[9] * D))) + T_sat = 0.5 * (n4[9] + D + - sqrt((n4[9] + D)**2 - 4.0 * (n4[8] + n4[9] * D))) # Make sure we aren't above saturation. (Relax this bound by .2 degrees # for deg C to K conversions.) @@ -273,38 +273,37 @@ def water_density(temperature, pressure=0.1013): # IAPWS region 1 parameters R_GAS_CONSTANT = 0.461526 # kJ / kg / K - _ref_p = 16.53 # MPa - _ref_T = 1386 # K - _n1f = [0.14632971213167, -0.84548187169114, -0.37563603672040e1, - 0.33855169168385e1, -0.95791963387872, 0.15772038513228, - -0.16616417199501e-1, 0.81214629983568e-3, 0.28319080123804e-3, - -0.60706301565874e-3, -0.18990068218419e-1, -0.32529748770505e-1, - -0.21841717175414e-1, -0.52838357969930e-4, -0.47184321073267e-3, - -0.30001780793026e-3, 0.47661393906987e-4, -0.44141845330846e-5, - -0.72694996297594e-15, -0.31679644845054e-4, -0.28270797985312e-5, - -0.85205128120103e-9, -0.22425281908000e-5, -0.65171222895601e-6, - -0.14341729937924e-12, -0.40516996860117e-6, -0.12734301741641e-8, - -0.17424871230634e-9, -0.68762131295531e-18, 0.14478307828521e-19, - 0.26335781662795e-22, -0.11947622640071e-22, 0.18228094581404e-23, - -0.93537087292458e-25] - _I1f = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, - 4, 4, 5, 8, 8, 21, 23, 29, 30, 31, 32] - _J1f = [-2, -1, 0, 1, 2, 3, 4, 5, -9, -7, -1, 0, 1, 3, -3, 0, 1, 3, 17, -4, - 0, 6, -5, -2, 10, -8, -11, -6, -29, -31, -38, -39, -40, -41] + ref_p = 16.53 # MPa + ref_T = 1386 # K + n1f = [0.14632971213167, -0.84548187169114, -0.37563603672040e1, + 0.33855169168385e1, -0.95791963387872, 0.15772038513228, + -0.16616417199501e-1, 0.81214629983568e-3, 0.28319080123804e-3, + -0.60706301565874e-3, -0.18990068218419e-1, -0.32529748770505e-1, + -0.21841717175414e-1, -0.52838357969930e-4, -0.47184321073267e-3, + -0.30001780793026e-3, 0.47661393906987e-4, -0.44141845330846e-5, + -0.72694996297594e-15, -0.31679644845054e-4, -0.28270797985312e-5, + -0.85205128120103e-9, -0.22425281908000e-5, -0.65171222895601e-6, + -0.14341729937924e-12, -0.40516996860117e-6, -0.12734301741641e-8, + -0.17424871230634e-9, -0.68762131295531e-18, 0.14478307828521e-19, + 0.26335781662795e-22, -0.11947622640071e-22, 0.18228094581404e-23, + -0.93537087292458e-25] + I1f = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, + 4, 4, 5, 8, 8, 21, 23, 29, 30, 31, 32] + J1f = [-2, -1, 0, 1, 2, 3, 4, 5, -9, -7, -1, 0, 1, 3, -3, 0, 1, 3, 17, -4, + 0, 6, -5, -2, 10, -8, -11, -6, -29, -31, -38, -39, -40, -41] # Nondimensionalize the pressure and temperature. - pi = pressure / _ref_p - tau = _ref_T / temperature + pi = pressure / ref_p + tau = ref_T / temperature # Compute the derivative of gamma (dimensionless Gibbs free energy) with # respect to pi. gamma1_pi = 0.0 - for i in range(34): - gamma1_pi -= (_n1f[i] * _I1f[i] * (7.1 - pi)**(_I1f[i] - 1) - * (tau - 1.222)**_J1f[i]) + for n, I, J in zip(n1f, I1f, J1f): + gamma1_pi -= n * I * (7.1 - pi)**(I - 1) * (tau - 1.222)**J # Compute the leading coefficient. This sets the units at - # 1 [MPa] * [kg K / kJ] / [1 / K] + # 1 [MPa] * [kg K / kJ] * [1 / K] # = 1e6 [N / m^2] * 1e-3 [kg K / N / m] * [1 / K] # = 1e3 [kg / m^3] # = 1 [g / cm^3] diff --git a/openmc/model/funcs.py b/openmc/model/funcs.py index b8984ada0..e974f93b1 100644 --- a/openmc/model/funcs.py +++ b/openmc/model/funcs.py @@ -8,6 +8,98 @@ from openmc.checkvalue import check_type, check_value import openmc.data +def borated_water(boron_ppm, temperature=293., pressure=0.1013, temp_unit='K', + press_unit='MPa', density=None, **kwargs): + """Return a Material with the composition of boron dissolved in water. + + The water density can be determined from a temperature and pressure, or it + can be set directly. + + The concentration of boron has no effect on the stoichiometric ratio of H + and O---they are fixed at 2-1. + + Parameters + ---------- + boron_ppm : float + The weight fraction in parts-per-million of elemental boron in the + water. + temperature : float + Temperature in [K] used to compute water density. + pressure : float + Pressure in [MPa] used to compute water density. + temp_unit : {'K', 'C', 'F'} + The units used for the `temperature` argument. + press_unit : {'MPa', 'psi'} + The units used for the `pressure` argument. + density : float + Water density in [g / cm^3]. If specified, this value overrides the + temperature and pressure arguments. + **kwargs + All keyword arguments are passed to the created Material object. + + Returns + ------- + openmc.Material + + """ + # Perform any necessary unit conversions. + check_value('temperature unit', temp_unit, ('K', 'C', 'F')) + if temp_unit == 'K': + T = temperature + elif temp_unit == 'C': + T = temperature + 273.15 + elif temp_unit == 'F': + T = (temperature + 459.67) * 5.0 / 9.0 + check_value('pressure unit', press_unit, ('MPa', 'psi')) + if press_unit == 'MPa': + P = pressure + elif press_unit == 'psi': + P = pressure * 0.006895 + + # Set the density of water, either from an explicitly given density or from + # temperature and pressure. + if density is not None: + water_density = density + else: + water_density = openmc.data.water_density(T, P) + + # Compute the density of the solution. + solution_density = water_density / (1 - boron_ppm * 1e-6) + + # Compute the molar mass of pure water. + hydrogen = openmc.Element('H') + oxygen = openmc.Element('O') + M_H2O = 0.0 + for iso_name, frac, junk in hydrogen.expand(2.0, 'ao'): + M_H2O += frac * openmc.data.atomic_mass(iso_name) + for iso_name, frac, junk in oxygen.expand(1.0, 'ao'): + M_H2O += frac * openmc.data.atomic_mass(iso_name) + + # Compute the molar mass of boron. + boron = openmc.Element('B') + M_B = 0.0 + for iso_name, frac, junk in boron.expand(1.0, 'ao'): + M_B += frac * openmc.data.atomic_mass(iso_name) + + # Compute the number fractions of each element. + frac_H2O = (1 - boron_ppm * 1e-6) / M_H2O + frac_H = 2 * frac_H2O + frac_O = frac_H2O + frac_B = boron_ppm * 1e-6 / M_B + + # Build the material. + if density is None: + out = openmc.Material(temperature=T, **kwargs) + else: + out = openmc.Material(**kwargs) + out.add_element('H', frac_H, 'ao') + out.add_element('O', frac_O, 'ao') + out.add_element('B', frac_B, 'ao') + out.set_density('g/cc', solution_density) + out.add_s_alpha_beta('c_H_in_H2O') + return out + + def get_rectangular_prism(width, height, axis='z', origin=(0., 0.), boundary_type='transmission', corner_radius=0.): """Get an infinite rectangular prism from four planar surfaces. @@ -253,100 +345,6 @@ def get_hexagonal_prism(edge_length=1., orientation='y', origin=(0., 0.), return prism -def make_borated_water(boron_ppm, temperature=293., pressure=0.1013, - temp_unit='K', press_unit='MPa', density=None, **kwargs): - """Return a Material with the composition of boron dissolved in water. - - The water density can be determined from a temperature and pressure, or it - can be set directly. - - The concentration of boron has no effect on the stoichometric ratio of H - and O---they are fixed at 2-1. - - Parameters - ---------- - boron_ppm : float - The weight fraction in parts-per-million of elemental boron in the - water. - temperature : float - Temperature in [K] used to compute water density. - pressure : float - Pressure in [MPa] used to compute water density. - temp_unit : str - The units used for the `temperature` argument. Valid units are 'K', - 'C', and 'F'. - press_unit : str - The units used for the `pressure` argument. Valid units are 'MPa' and - 'psi'. - density : float - Water density in [g / cm^3]. If specified, this value overrides the - temperature and pressure arguments. - **kwargs - All keyword arguments are passed to the created Material object. - - Returns - ------- - openmc.Material - - """ - # Perform any necessary unit conversions. - check_value('temperature unit', temp_unit, ('K', 'C', 'F')) - if temp_unit == 'K': - T = temperature - elif temp_unit == 'C': - T = temperature + 273.15 - elif temp_unit == 'F': - T = (temperature + 459.67) * 5.0 / 9.0 - check_value('pressure unit', press_unit, ('MPa', 'psi')) - if press_unit == 'MPa': - P = pressure - elif press_unit == 'psi': - P = pressure * 0.006895 - - # Set the density of water, either from an explicitly given density or from - # temperature and pressure. - if density is not None: - water_density = density - else: - water_density = openmc.data.water_density(T, P) - - # Compute the density of the solution. - solution_density = water_density / (1 - boron_ppm * 1e-6) - - # Compute the molar mass of pure water. - hydrogen = openmc.Element('H') - oxygen = openmc.Element('O') - M_H2O = 0.0 - for iso_name, frac, junk in hydrogen.expand(2.0, 'ao'): - M_H2O += frac * openmc.data.atomic_mass(iso_name) - for iso_name, frac, junk in oxygen.expand(1.0, 'ao'): - M_H2O += frac * openmc.data.atomic_mass(iso_name) - - # Compute the molar mass of boron. - boron = openmc.Element('B') - M_B = 0.0 - for iso_name, frac, junk in boron.expand(1.0, 'ao'): - M_B += frac * openmc.data.atomic_mass(iso_name) - - # Compute the number fractions of each element. - frac_H2O = (1 - boron_ppm * 1e-6) / M_H2O - frac_H = 2 * frac_H2O - frac_O = frac_H2O - frac_B = boron_ppm * 1e-6 / M_B - - # Build the material. - if density is None: - out = openmc.Material(temperature=T, **kwargs) - else: - out = openmc.Material(**kwargs) - out.add_element('H', frac_H, 'ao') - out.add_element('O', frac_O, 'ao') - out.add_element('B', frac_B, 'ao') - out.set_density('g/cc', solution_density) - out.add_s_alpha_beta('c_H_in_H2O') - return out - - def subdivide(surfaces): """Create regions separated by a series of surfaces. diff --git a/tests/unit_tests/test_material.py b/tests/unit_tests/test_material.py index 295153a9c..226521541 100644 --- a/tests/unit_tests/test_material.py +++ b/tests/unit_tests/test_material.py @@ -143,7 +143,7 @@ def test_materials(run_in_tmpdir): def test_borated_water(): # Test against reference values from the BEAVRS benchmark. - m = openmc.model.make_borated_water(975, 566.5, 15.51, material_id=50) + m = openmc.model.borated_water(975, 566.5, 15.51, material_id=50) assert m.density == pytest.approx(0.7405, 1e-3) assert m.temperature == pytest.approx(566.5) assert m._sab[0][0] == 'c_H_in_H2O' @@ -155,13 +155,13 @@ def test_borated_water(): assert m.id == 50 # Test the Celsius conversion. - m = openmc.model.make_borated_water(975, 293.35, 15.51, 'C') + m = openmc.model.borated_water(975, 293.35, 15.51, 'C') assert m.density == pytest.approx(0.7405, 1e-3) # Test Fahrenheit and psi conversions. - m = openmc.model.make_borated_water(975, 560.0, 2250.0, 'F', 'psi') + m = openmc.model.borated_water(975, 560.0, 2250.0, 'F', 'psi') assert m.density == pytest.approx(0.7405, 1e-3) # Test the density override - m = openmc.model.make_borated_water(975, 566.5, 15.51, density=0.9) + m = openmc.model.borated_water(975, 566.5, 15.51, density=0.9) assert m.density == pytest.approx(0.9, 1e-3)