• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

openmc-dev / openmc / 29375619482

14 Jul 2026 11:16PM UTC coverage: 81.376% (+0.08%) from 81.295%
29375619482

Pull #3971

github

web-flow
Merge 502d76c93 into e783e0147
Pull Request #3971: Delta tracking

18579 of 26880 branches covered (69.12%)

Branch coverage included in aggregate %.

615 of 661 new or added lines in 20 files covered. (93.04%)

76 existing lines in 3 files now uncovered.

60003 of 69687 relevant lines covered (86.1%)

49610935.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.34
/openmc/data/data.py
1
from __future__ import annotations
11✔
2

3
import itertools
11✔
4
import json
11✔
5
import os
11✔
6
import re
11✔
7
from pathlib import Path
11✔
8
from math import sqrt, log
11✔
9
from typing import TYPE_CHECKING, Literal
11✔
10
from warnings import warn
11✔
11

12
from endf.data import (ATOMIC_NUMBER, ATOMIC_SYMBOL, ELEMENT_SYMBOL,
11✔
13
                       EV_PER_MEV, K_BOLTZMANN, gnds_name, zam)
14

15
import openmc
11✔
16
from openmc.checkvalue import PathLike
11✔
17

18
if TYPE_CHECKING:
19
    from openmc.deplete import Chain
20

21
gnds_name.__module__ = __name__
11✔
22
zam.__module__ = __name__
11✔
23

24
# Isotopic abundances from Meija J, Coplen T B, et al, "Isotopic compositions
25
# of the elements 2013 (IUPAC Technical Report)", Pure. Appl. Chem. 88 (3),
26
# pp. 293-306 (2013). The "representative isotopic abundance" values from
27
# column 9 are used except where an interval is given, in which case the
28
# "best measurement" is used.
29
# Note that the abundances are given as atomic fractions!
30
NATURAL_ABUNDANCE = {
11✔
31
    'H1': 0.99984426, 'H2': 0.00015574, 'He3': 0.000002,
32
    'He4': 0.999998, 'Li6': 0.07589, 'Li7': 0.92411,
33
    'Be9': 1.0, 'B10': 0.1982, 'B11': 0.8018,
34
    'C12': 0.988922, 'C13': 0.011078, 'N14': 0.996337,
35
    'N15': 0.003663, 'O16': 0.9976206, 'O17': 0.000379,
36
    'O18': 0.0020004, 'F19': 1.0, 'Ne20': 0.9048,
37
    'Ne21': 0.0027, 'Ne22': 0.0925, 'Na23': 1.0,
38
    'Mg24': 0.78951, 'Mg25': 0.1002, 'Mg26': 0.11029,
39
    'Al27': 1.0, 'Si28': 0.9222968, 'Si29': 0.0468316,
40
    'Si30': 0.0308716, 'P31': 1.0, 'S32': 0.9504074,
41
    'S33': 0.0074869, 'S34': 0.0419599, 'S36': 0.0001458,
42
    'Cl35': 0.757647, 'Cl37': 0.242353, 'Ar36': 0.003336,
43
    'Ar38': 0.000629, 'Ar40': 0.996035, 'K39': 0.932581,
44
    'K40': 0.000117, 'K41': 0.067302, 'Ca40': 0.96941,
45
    'Ca42': 0.00647, 'Ca43': 0.00135, 'Ca44': 0.02086,
46
    'Ca46': 0.00004, 'Ca48': 0.00187, 'Sc45': 1.0,
47
    'Ti46': 0.0825, 'Ti47': 0.0744, 'Ti48': 0.7372,
48
    'Ti49': 0.0541, 'Ti50': 0.0518, 'V50': 0.0025,
49
    'V51': 0.9975, 'Cr50': 0.04345, 'Cr52': 0.83789,
50
    'Cr53': 0.09501, 'Cr54': 0.02365, 'Mn55': 1.0,
51
    'Fe54': 0.05845, 'Fe56': 0.91754, 'Fe57': 0.02119,
52
    'Fe58': 0.00282, 'Co59': 1.0, 'Ni58': 0.680769,
53
    'Ni60': 0.262231, 'Ni61': 0.011399, 'Ni62': 0.036345,
54
    'Ni64': 0.009256, 'Cu63': 0.6915, 'Cu65': 0.3085,
55
    'Zn64': 0.4917, 'Zn66': 0.2773, 'Zn67': 0.0404,
56
    'Zn68': 0.1845, 'Zn70': 0.0061, 'Ga69': 0.60108,
57
    'Ga71': 0.39892, 'Ge70': 0.2052, 'Ge72': 0.2745,
58
    'Ge73': 0.0776, 'Ge74': 0.3652, 'Ge76': 0.0775,
59
    'As75': 1.0, 'Se74': 0.0086, 'Se76': 0.0923,
60
    'Se77': 0.076, 'Se78': 0.2369, 'Se80': 0.498,
61
    'Se82': 0.0882, 'Br79': 0.50686, 'Br81': 0.49314,
62
    'Kr78': 0.00355, 'Kr80': 0.02286, 'Kr82': 0.11593,
63
    'Kr83': 0.115, 'Kr84': 0.56987, 'Kr86': 0.17279,
64
    'Rb85': 0.7217, 'Rb87': 0.2783, 'Sr84': 0.0056,
65
    'Sr86': 0.0986, 'Sr87': 0.07, 'Sr88': 0.8258,
66
    'Y89': 1.0, 'Zr90': 0.5145, 'Zr91': 0.1122,
67
    'Zr92': 0.1715, 'Zr94': 0.1738, 'Zr96': 0.028,
68
    'Nb93': 1.0, 'Mo92': 0.14649, 'Mo94': 0.09187,
69
    'Mo95': 0.15873, 'Mo96': 0.16673, 'Mo97': 0.09582,
70
    'Mo98': 0.24292, 'Mo100': 0.09744, 'Ru96': 0.0554,
71
    'Ru98': 0.0187, 'Ru99': 0.1276, 'Ru100': 0.126,
72
    'Ru101': 0.1706, 'Ru102': 0.3155, 'Ru104': 0.1862,
73
    'Rh103': 1.0, 'Pd102': 0.0102, 'Pd104': 0.1114,
74
    'Pd105': 0.2233, 'Pd106': 0.2733, 'Pd108': 0.2646,
75
    'Pd110': 0.1172, 'Ag107': 0.51839, 'Ag109': 0.48161,
76
    'Cd106': 0.01245, 'Cd108': 0.00888, 'Cd110': 0.1247,
77
    'Cd111': 0.12795, 'Cd112': 0.24109, 'Cd113': 0.12227,
78
    'Cd114': 0.28754, 'Cd116': 0.07512, 'In113': 0.04281,
79
    'In115': 0.95719, 'Sn112': 0.0097, 'Sn114': 0.0066,
80
    'Sn115': 0.0034, 'Sn116': 0.1454, 'Sn117': 0.0768,
81
    'Sn118': 0.2422, 'Sn119': 0.0859, 'Sn120': 0.3258,
82
    'Sn122': 0.0463, 'Sn124': 0.0579, 'Sb121': 0.5721,
83
    'Sb123': 0.4279, 'Te120': 0.0009, 'Te122': 0.0255,
84
    'Te123': 0.0089, 'Te124': 0.0474, 'Te125': 0.0707,
85
    'Te126': 0.1884, 'Te128': 0.3174, 'Te130': 0.3408,
86
    'I127': 1.0, 'Xe124': 0.00095, 'Xe126': 0.00089,
87
    'Xe128': 0.0191, 'Xe129': 0.26401, 'Xe130': 0.04071,
88
    'Xe131': 0.21232, 'Xe132': 0.26909, 'Xe134': 0.10436,
89
    'Xe136': 0.08857, 'Cs133': 1.0, 'Ba130': 0.0011,
90
    'Ba132': 0.001, 'Ba134': 0.0242, 'Ba135': 0.0659,
91
    'Ba136': 0.0785, 'Ba137': 0.1123, 'Ba138': 0.717,
92
    'La138': 0.0008881, 'La139': 0.9991119, 'Ce136': 0.00186,
93
    'Ce138': 0.00251, 'Ce140': 0.88449, 'Ce142': 0.11114,
94
    'Pr141': 1.0, 'Nd142': 0.27153, 'Nd143': 0.12173,
95
    'Nd144': 0.23798, 'Nd145': 0.08293, 'Nd146': 0.17189,
96
    'Nd148': 0.05756, 'Nd150': 0.05638, 'Sm144': 0.0308,
97
    'Sm147': 0.15, 'Sm148': 0.1125, 'Sm149': 0.1382,
98
    'Sm150': 0.0737, 'Sm152': 0.2674, 'Sm154': 0.2274,
99
    'Eu151': 0.4781, 'Eu153': 0.5219, 'Gd152': 0.002,
100
    'Gd154': 0.0218, 'Gd155': 0.148, 'Gd156': 0.2047,
101
    'Gd157': 0.1565, 'Gd158': 0.2484, 'Gd160': 0.2186,
102
    'Tb159': 1.0, 'Dy156': 0.00056, 'Dy158': 0.00095,
103
    'Dy160': 0.02329, 'Dy161': 0.18889, 'Dy162': 0.25475,
104
    'Dy163': 0.24896, 'Dy164': 0.2826, 'Ho165': 1.0,
105
    'Er162': 0.00139, 'Er164': 0.01601, 'Er166': 0.33503,
106
    'Er167': 0.22869, 'Er168': 0.26978, 'Er170': 0.1491,
107
    'Tm169': 1.0, 'Yb168': 0.00123, 'Yb170': 0.02982,
108
    'Yb171': 0.14086, 'Yb172': 0.21686, 'Yb173': 0.16103,
109
    'Yb174': 0.32025, 'Yb176': 0.12995, 'Lu175': 0.97401,
110
    'Lu176': 0.02599, 'Hf174': 0.0016, 'Hf176': 0.0526,
111
    'Hf177': 0.186, 'Hf178': 0.2728, 'Hf179': 0.1362,
112
    'Hf180': 0.3508, 'Ta180_m1': 0.0001201, 'Ta181': 0.9998799,
113
    'W180': 0.0012, 'W182': 0.265, 'W183': 0.1431,
114
    'W184': 0.3064, 'W186': 0.2843, 'Re185': 0.374,
115
    'Re187': 0.626, 'Os184': 0.0002, 'Os186': 0.0159,
116
    'Os187': 0.0196, 'Os188': 0.1324, 'Os189': 0.1615,
117
    'Os190': 0.2626, 'Os192': 0.4078, 'Ir191': 0.373,
118
    'Ir193': 0.627, 'Pt190': 0.00012, 'Pt192': 0.00782,
119
    'Pt194': 0.32864, 'Pt195': 0.33775, 'Pt196': 0.25211,
120
    'Pt198': 0.07356, 'Au197': 1.0, 'Hg196': 0.0015,
121
    'Hg198': 0.1004, 'Hg199': 0.1694, 'Hg200': 0.2314,
122
    'Hg201': 0.1317, 'Hg202': 0.2974, 'Hg204': 0.0682,
123
    'Tl203': 0.29524, 'Tl205': 0.70476, 'Pb204': 0.014,
124
    'Pb206': 0.241, 'Pb207': 0.221, 'Pb208': 0.524,
125
    'Bi209': 1.0, 'Th230': 0.0002, 'Th232': 0.9998,
126
    'Pa231': 1.0, 'U234': 0.000054, 'U235': 0.007204,
127
    'U238': 0.992742
128
}
129

130
DADZ = {
11✔
131
    '(n,2nd)': (-3, -1),
132
    '(n,2n)': (-1, 0),
133
    '(n,3n)': (-2, 0),
134
    '(n,na)': (-4, -2),
135
    '(n,n3a)': (-12, -6),
136
    '(n,2na)': (-5, -2),
137
    '(n,3na)': (-6, -2),
138
    '(n,np)': (-1, -1),
139
    '(n,n2a)': (-8, -4),
140
    '(n,2n2a)': (-9, -4),
141
    '(n,nd)': (-2, -1),
142
    '(n,nt)': (-3, -1),
143
    '(n,n3He)': (-3, -2),
144
    '(n,nd2a)': (-10, -5),
145
    '(n,nt2a)': (-11, -5),
146
    '(n,4n)': (-3, 0),
147
    '(n,2np)': (-2, -1),
148
    '(n,3np)': (-3, -1),
149
    '(n,n2p)': (-2, -2),
150
    '(n,npa)': (-5, -3),
151
    '(n,gamma)': (1, 0),
152
    '(n,p)': (0, -1),
153
    '(n,d)': (-1, -1),
154
    '(n,t)': (-2, -1),
155
    '(n,3He)': (-2, -2),
156
    '(n,a)': (-3, -2),
157
    '(n,2a)': (-7, -4),
158
    '(n,3a)': (-11, -6),
159
    '(n,2p)': (-1, -2),
160
    '(n,pa)': (-4, -3),
161
    '(n,t2a)': (-10, -5),
162
    '(n,d2a)': (-9, -5),
163
    '(n,pd)': (-2, -2),
164
    '(n,pt)': (-3, -2),
165
    '(n,da)': (-5, -3),
166
    '(n,5n)': (-4, 0),
167
    '(n,6n)': (-5, 0),
168
    '(n,2nt)': (-4, -1),
169
    '(n,ta)': (-6, -3),
170
    '(n,4np)': (-4, -1),
171
    '(n,3nd)': (-4, -1),
172
    '(n,nda)': (-6, -3),
173
    '(n,2npa)': (-6, -3),
174
    '(n,7n)': (-6, 0),
175
    '(n,8n)': (-7, 0),
176
    '(n,5np)': (-5, -1),
177
    '(n,6np)': (-6, -1),
178
    '(n,7np)': (-7, -1),
179
    '(n,4na)': (-7, -2),
180
    '(n,5na)': (-8, -2),
181
    '(n,6na)': (-9, -2),
182
    '(n,7na)': (-10, -2),
183
    '(n,4nd)': (-5, -1),
184
    '(n,5nd)': (-6, -1),
185
    '(n,6nd)': (-7, -1),
186
    '(n,3nt)': (-5, -1),
187
    '(n,4nt)': (-6, -1),
188
    '(n,5nt)': (-7, -1),
189
    '(n,6nt)': (-8, -1),
190
    '(n,2n3He)': (-4, -2),
191
    '(n,3n3He)': (-5, -2),
192
    '(n,4n3He)': (-6, -2),
193
    '(n,3n2p)': (-4, -2),
194
    '(n,3n2a)': (-10, -4),
195
    '(n,3npa)': (-7, -3),
196
    '(n,dt)': (-4, -2),
197
    '(n,npd)': (-3, -2),
198
    '(n,npt)': (-4, -2),
199
    '(n,ndt)': (-5, -2),
200
    '(n,np3He)': (-4, -3),
201
    '(n,nd3He)': (-5, -3),
202
    '(n,nt3He)': (-6, -3),
203
    '(n,nta)': (-7, -3),
204
    '(n,2n2p)': (-3, -2),
205
    '(n,p3He)': (-4, -3),
206
    '(n,d3He)': (-5, -3),
207
    '(n,3Hea)': (-6, -4),
208
    '(n,4n2p)': (-5, -2),
209
    '(n,4n2a)': (-11, -4),
210
    '(n,4npa)': (-8, -3),
211
    '(n,3p)': (-2, -3),
212
    '(n,n3p)': (-3, -3),
213
    '(n,3n2pa)': (-8, -4),
214
    '(n,5n2p)': (-6, -2),
215
}
216

217
# Values here are from the Committee on Data for Science and Technology
218
# (CODATA) 2018 recommendation (https://physics.nist.gov/cuu/Constants/).
219

220
# Unit conversions
221
JOULE_PER_EV = 1.602176634e-19
11✔
222

223
# Avogadro's constant
224
AVOGADRO = 6.02214076e23
11✔
225

226
# Neutron mass in units of amu
227
NEUTRON_MASS = 1.00866491595
11✔
228

229
# Used in atomic_mass function as a cache
230
_ATOMIC_MASS: dict[str, float] = {}
11✔
231

232
# Used in half_life function as a cache
233
_HALF_LIFE: dict[str, float] = {}
11✔
234
_LOG_TWO = log(2.0)
11✔
235

236
def atomic_mass(isotope):
11✔
237
    """Return atomic mass of isotope in atomic mass units.
238

239
    Atomic mass data comes from the `Atomic Mass Evaluation 2020
240
    <https://doi.org/10.1088/1674-1137/abddaf>`_.
241

242
    Parameters
243
    ----------
244
    isotope : str
245
        Name of isotope, e.g., 'Pu239'
246

247
    Returns
248
    -------
249
    float
250
        Atomic mass of isotope in [amu]
251

252
    """
253
    if not _ATOMIC_MASS:
11✔
254

255
        # Load data from AME2020 file
256
        mass_file = os.path.join(os.path.dirname(__file__), 'mass_1.mas20.txt')
11✔
257
        with open(mass_file, 'r') as ame:
11✔
258
            # Read lines in file starting at line 37
259
            for line in itertools.islice(ame, 36, None):
11✔
260
                name = f'{line[20:22].strip()}{int(line[16:19])}'
11✔
261
                mass = float(line[106:109]) + 1e-6*float(
11✔
262
                    line[110:116] + '.' + line[117:123])
263
                _ATOMIC_MASS[name.lower()] = mass
11✔
264

265
        # For isotopes found in some libraries that represent all natural
266
        # isotopes of their element (e.g. C0), calculate the atomic mass as
267
        # the sum of the atomic mass times the natural abundance of the isotopes
268
        # that make up the element.
269
        for element in ['C', 'Zn', 'Pt', 'Os', 'Tl', 'V']:
11✔
270
            isotope_zero = element.lower() + '0'
11✔
271
            _ATOMIC_MASS[isotope_zero] = 0.
11✔
272
            for iso, abundance in isotopes(element):
11✔
273
                _ATOMIC_MASS[isotope_zero] += abundance * _ATOMIC_MASS[iso.lower()]
11✔
274

275
    # Get rid of metastable information
276
    if '_' in isotope:
11✔
277
        isotope = isotope[:isotope.find('_')]
11✔
278

279
    return _ATOMIC_MASS[isotope.lower()]
11✔
280

281

282
def atomic_weight(element):
11✔
283
    """Return atomic weight of an element in atomic mass units.
284

285
    Computes an average of the atomic mass of each of element's naturally
286
    occurring isotopes weighted by their relative abundance.
287

288
    Parameters
289
    ----------
290
    element : str
291
        Element symbol (e.g., 'H') or name (e.g., 'helium')
292

293
    Returns
294
    -------
295
    float
296
        Atomic weight of element in [amu]
297

298
    """
299
    weight = 0.
11✔
300
    for nuclide, abundance in isotopes(element):
11✔
301
        weight += atomic_mass(nuclide) * abundance
11✔
302
    if weight > 0.:
11✔
303
        return weight
11✔
304
    else:
305
        raise ValueError(f"No naturally-occurring isotopes for element '{element}'.")
11✔
306

307

308
def half_life(
11✔
309
    isotope: str,
310
    chain_file: Literal[False] | None | PathLike | Chain = False
311
) -> float | None:
312
    """Return half-life of isotope in seconds or None if isotope is stable
313

314
    By default, half-life values are from the `ENDF/B-VIII.0 decay sublibrary
315
    <https://www.nndc.bnl.gov/endf-b8.0/download.html>`_. A depletion chain can
316
    also be used as the source of half-life values.
317

318
    .. versionadded:: 0.13.1
319

320
    .. versionchanged:: 0.15.4
321
        Added the ``chain_file`` argument.
322

323
    Parameters
324
    ----------
325
    isotope : str
326
        Name of isotope, e.g., 'Pu239'
327
    chain_file : False, None, PathLike, or openmc.deplete.Chain, optional
328
        Source of half-life values. If ``False``, only ENDF/B-VIII.0 data is
329
        used. If ``None``, the chain specified by
330
        ``openmc.config['chain_file']`` is used when available. If a path or
331
        :class:`openmc.deplete.Chain` is given, that chain is used. For ``None``
332
        or an explicit chain, nuclides absent from the chain fall back to
333
        ENDF/B-VIII.0 data.
334

335
    Returns
336
    -------
337
    float or None
338
        Half-life of isotope in [s], or None if the isotope is stable
339

340
    """
341
    if chain_file is not False:
11✔
342
        if chain_file is not None or openmc.config.get('chain_file') is not None:
11✔
343
            # Local import avoids a circular dependency
344
            from openmc.deplete.chain import _get_chain
11✔
345
            chain = _get_chain(chain_file)
11✔
346
            if isotope in chain:
11✔
347
                return chain[isotope].half_life
11✔
348

349
    global _HALF_LIFE
350
    if not _HALF_LIFE:
11✔
351
        # Load ENDF/B-VIII.0 data from JSON file
352
        half_life_path = Path(__file__).with_name('half_life.json')
11✔
353
        _HALF_LIFE = json.loads(half_life_path.read_text())
11✔
354

355
    return _HALF_LIFE.get(isotope.lower())
11✔
356

357

358
def decay_constant(
11✔
359
    isotope: str,
360
    chain_file: Literal[False] | None | PathLike | Chain = False
361
) -> float:
362
    """Return decay constant of isotope in [s^-1]
363

364
    Decay constants are based on half-life values from the
365
    :func:`~openmc.data.half_life` function. When the isotope is stable, a decay
366
    constant of zero is returned.
367

368
    .. versionadded:: 0.13.1
369

370
    .. versionchanged:: 0.15.4
371
        Added the ``chain_file`` argument.
372

373
    Parameters
374
    ----------
375
    isotope : str
376
        Name of isotope, e.g., 'Pu239'
377
    chain_file : False, None, PathLike, or openmc.deplete.Chain, optional
378
        Source of half-life values. If ``False``, only ENDF/B-VIII.0 data is
379
        used. If ``None``, the chain specified by
380
        ``openmc.config['chain_file']`` is used when available. If a path or
381
        :class:`openmc.deplete.Chain` is given, that chain is used. For ``None``
382
        or an explicit chain, nuclides absent from the chain fall back to
383
        ENDF/B-VIII.0 data.
384

385
    Returns
386
    -------
387
    float
388
        Decay constant of isotope in [s^-1]
389

390
    See also
391
    --------
392
    openmc.data.half_life
393

394
    """
395
    t = half_life(isotope, chain_file)
11✔
396
    return _LOG_TWO / t if t else 0.0
11✔
397

398

399
def water_density(temperature, pressure=0.1013):
11✔
400
    """Return the density of liquid water at a given temperature and pressure.
401

402
    The density is calculated from a polynomial fit using equations and values
403
    from the 2012 version of the IAPWS-IF97 formulation.  Only the equations
404
    for region 1 are implemented here.  Region 1 is limited to liquid water
405
    below 100 [MPa] with a temperature above 273.15 [K], below 623.15 [K], and
406
    below saturation.
407

408
    Reference: International Association for the Properties of Water and Steam,
409
    "Revised Release on the IAPWS Industrial Formulation 1997 for the
410
    Thermodynamic Properties of Water and Steam", IAPWS R7-97(2012).
411

412
    Parameters
413
    ----------
414
    temperature : float
415
        Water temperature in units of [K]
416
    pressure : float
417
        Water pressure in units of [MPa]
418

419
    Returns
420
    -------
421
    float
422
        Water density in units of [g/cm^3]
423

424
    """
425

426
    # Make sure the temperature and pressure are inside the min/max region 1
427
    # bounds.  (Relax the 273.15 bound to 273 in case a user wants 0 deg C data
428
    # but they only use 3 digits for their conversion to K.)
429
    if pressure > 100.0:
11✔
UNCOV
430
        warn("Results are not valid for pressures above 100 MPa.")
×
431
    elif pressure < 0.0:
11✔
UNCOV
432
        raise ValueError("Pressure must be positive.")
×
433
    if temperature < 273:
11✔
UNCOV
434
        warn("Results are not valid for temperatures below 273.15 K.")
×
435
    elif temperature > 623.15:
11✔
UNCOV
436
        warn("Results are not valid for temperatures above 623.15 K.")
×
437
    elif temperature <= 0.0:
11✔
UNCOV
438
        raise ValueError('Temperature must be positive.')
×
439

440
    # IAPWS region 4 parameters
441
    n4 = [0.11670521452767e4, -0.72421316703206e6, -0.17073846940092e2,
11✔
442
          0.12020824702470e5, -0.32325550322333e7, 0.14915108613530e2,
443
          -0.48232657361591e4, 0.40511340542057e6, -0.23855557567849,
444
          0.65017534844798e3]
445

446
    # Compute the saturation temperature at the given pressure.
447
    beta = pressure**(0.25)
11✔
448
    E = beta**2 + n4[2] * beta + n4[5]
11✔
449
    F = n4[0] * beta**2 + n4[3] * beta + n4[6]
11✔
450
    G = n4[1] * beta**2 + n4[4] * beta + n4[7]
11✔
451
    D = 2.0 * G / (-F - sqrt(F**2 - 4 * E * G))
11✔
452
    T_sat = 0.5 * (n4[9] + D
11✔
453
                   - sqrt((n4[9] + D)**2  - 4.0 * (n4[8] + n4[9] * D)))
454

455
    # Make sure we aren't above saturation.  (Relax this bound by .2 degrees
456
    # for deg C to K conversions.)
457
    if temperature > T_sat + 0.2:
11✔
UNCOV
458
        warn("Results are not valid for temperatures above saturation "
×
459
             "(above the boiling point).")
460

461
    # IAPWS region 1 parameters
462
    R_GAS_CONSTANT = 0.461526  # kJ / kg / K
11✔
463
    ref_p = 16.53  # MPa
11✔
464
    ref_T = 1386  # K
11✔
465
    n1f = [0.14632971213167, -0.84548187169114, -0.37563603672040e1,
11✔
466
           0.33855169168385e1, -0.95791963387872, 0.15772038513228,
467
           -0.16616417199501e-1, 0.81214629983568e-3, 0.28319080123804e-3,
468
           -0.60706301565874e-3, -0.18990068218419e-1, -0.32529748770505e-1,
469
           -0.21841717175414e-1, -0.52838357969930e-4, -0.47184321073267e-3,
470
           -0.30001780793026e-3, 0.47661393906987e-4, -0.44141845330846e-5,
471
           -0.72694996297594e-15, -0.31679644845054e-4, -0.28270797985312e-5,
472
           -0.85205128120103e-9, -0.22425281908000e-5, -0.65171222895601e-6,
473
           -0.14341729937924e-12, -0.40516996860117e-6, -0.12734301741641e-8,
474
           -0.17424871230634e-9, -0.68762131295531e-18, 0.14478307828521e-19,
475
           0.26335781662795e-22, -0.11947622640071e-22, 0.18228094581404e-23,
476
           -0.93537087292458e-25]
477
    I1f = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4,
11✔
478
           4, 4, 5, 8, 8, 21, 23, 29, 30, 31, 32]
479
    J1f = [-2, -1, 0, 1, 2, 3, 4, 5, -9, -7, -1, 0, 1, 3, -3, 0, 1, 3, 17, -4,
11✔
480
           0, 6, -5, -2, 10, -8, -11, -6, -29, -31, -38, -39, -40, -41]
481

482
    # Nondimensionalize the pressure and temperature.
483
    pi = pressure / ref_p
11✔
484
    tau = ref_T / temperature
11✔
485

486
    # Compute the derivative of gamma (dimensionless Gibbs free energy) with
487
    # respect to pi.
488
    gamma1_pi = 0.0
11✔
489
    for n, I, J in zip(n1f, I1f, J1f):
11✔
490
        gamma1_pi -= n * I * (7.1 - pi)**(I - 1) * (tau - 1.222)**J
11✔
491

492
    # Compute the leading coefficient.  This sets the units at
493
    #   1 [MPa] * [kg K / kJ] * [1 / K]
494
    # = 1e6 [N / m^2] * 1e-3 [kg K / N / m] * [1 / K]
495
    # = 1e3 [kg / m^3]
496
    # = 1 [g / cm^3]
497
    coeff = pressure / R_GAS_CONSTANT / temperature
11✔
498

499
    # Compute and return the density.
500
    return coeff / pi / gamma1_pi
11✔
501

502

503
def _get_element_symbol(element: str) -> str:
11✔
504
    if len(element) > 2:
11✔
505
        symbol = ELEMENT_SYMBOL.get(element.lower())
11✔
506
        if symbol is None:
11✔
507
            raise ValueError(f'Element name "{element}" not recognized')
11✔
508
        return symbol
11✔
509
    else:
510
        return element
11✔
511

512

513
def isotopes(element: str) -> list[tuple[str, float]]:
11✔
514
    """Return naturally occurring isotopes and their abundances
515

516
    .. versionadded:: 0.12.1
517

518
    Parameters
519
    ----------
520
    element : str
521
        Element symbol (e.g., 'H') or name (e.g., 'helium')
522

523
    Returns
524
    -------
525
    list
526
        A list of tuples of (isotope, abundance)
527

528
    Raises
529
    ------
530
    ValueError
531
        If the element name is not recognized
532

533
    """
534
    element = _get_element_symbol(element)
11✔
535

536
    # Get the nuclides present in nature
537
    result = []
11✔
538
    for kv in NATURAL_ABUNDANCE.items():
11✔
539
        if re.match(r'{}\d+'.format(element), kv[0]):
11✔
540
            result.append(kv)
11✔
541

542
    return result
11✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc