From 61a84d820362f0f745a09ffcba86af567aae18ab Mon Sep 17 00:00:00 2001 From: Jan-Lukas Wynen Date: Mon, 19 Feb 2024 17:03:55 +0100 Subject: [PATCH 1/5] Add atoms.scattering_params --- docs/api-reference/index.md | 1 + docs/bibliography.bib | 14 + pyproject.toml | 2 +- src/scippneutron/__init__.py | 1 + src/scippneutron/atoms/__init__.py | 91 +++++ .../atoms/scattering_parameters.csv | 371 ++++++++++++++++++ tests/atoms/test_atoms.py | 70 ++++ 7 files changed, 549 insertions(+), 1 deletion(-) create mode 100644 src/scippneutron/atoms/__init__.py create mode 100644 src/scippneutron/atoms/scattering_parameters.csv create mode 100644 tests/atoms/test_atoms.py diff --git a/docs/api-reference/index.md b/docs/api-reference/index.md index 7a7055906..9cbb95491 100644 --- a/docs/api-reference/index.md +++ b/docs/api-reference/index.md @@ -68,6 +68,7 @@ and possible confusion of `theta` (from Bagg's law) with `theta` in spherical co :template: module-template.rst :recursive: + atoms conversion io logging diff --git a/docs/bibliography.bib b/docs/bibliography.bib index d1ff62bf9..8c1b9a8e4 100644 --- a/docs/bibliography.bib +++ b/docs/bibliography.bib @@ -20,3 +20,17 @@ @article{busing:1967 volume = {22}, pages = {457--464}, } + +@article{sears:1992, + author = {Varley F. Sears}, + title = {Neutron scattering lengths and cross sections}, + journal = {Neutron News}, + volume = {3}, + number = {3}, + pages = {26-37}, + year = {1992}, + publisher = {Taylor & Francis}, + doi = {10.1080/10448639208218770}, + URL = {https://doi.org/10.1080/10448639208218770}, + eprint = {https://doi.org/10.1080/10448639208218770} +} diff --git a/pyproject.toml b/pyproject.toml index 5f41c9380..302c7be31 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -95,7 +95,7 @@ exclude_dirs = ["docs/conf.py", "tests", "tools"] [tool.codespell] ignore-words-list = "elemt" -skip = "./.git,./.tox,*/.virtual_documents,*/.ipynb_checkpoints,*.pdf,*.svg" +skip = "./.git,./.tox,*/.virtual_documents,*/.ipynb_checkpoints,*.pdf,*.svg,*.csv" [tool.black] skip-string-normalization = true diff --git a/src/scippneutron/__init__.py b/src/scippneutron/__init__.py index f15ae719c..c7167340f 100644 --- a/src/scippneutron/__init__.py +++ b/src/scippneutron/__init__.py @@ -41,6 +41,7 @@ from .instrument_view import instrument_view from .io.nexus.load_nexus import load_nexus, load_nexus_json from .data_streaming.data_stream import data_stream +from . import atoms from . import data del importlib diff --git a/src/scippneutron/atoms/__init__.py b/src/scippneutron/atoms/__init__.py new file mode 100644 index 000000000..7df522d01 --- /dev/null +++ b/src/scippneutron/atoms/__init__.py @@ -0,0 +1,91 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright (c) 2023 Scipp contributors (https://github.com/scipp) +"""Parameters for neutron interactions with atoms.""" + +import importlib.resources +from functools import lru_cache +from typing import Optional, TextIO + +import scipp as sc + + +def _open_scattering_parameters_file() -> TextIO: + return ( + importlib.resources.files('scippneutron.atoms') + .joinpath('scattering_parameters.csv') + .open('r') + ) + + +@lru_cache() +def scattering_params(isotope: str) -> dict[str, Optional[sc.Variable]]: + """Return the scattering parameters for the given element / isotope. + + Provides access to the scattering lengths and cross-sections of neutrons + with a given element or isotope. + Values have been retrieved at 2024-02-19T17:00:00Z from the list at + https://www.ncnr.nist.gov/resources/n-lengths/list.html + which is based on :cite:`sears:1992`. + + Parameters + ---------- + isotope: + Name of the element or isotope. + For example, 'H', '3He', 'V', '50V'. + + Returns + ------- + : + Dict with the scattering parameters. + A value can be ``None`` if the parameter is not available. + Scattering lengths are split into real and imaginary parts. + + Keys: + + - ``bound_coherent_scattering_length_re`` + - ``bound_coherent_scattering_length_im`` + - ``bound_incoherent_scattering_length_re`` + - ``bound_incoherent_scattering_length_im`` + - ``bound_coherent_scattering_cross_section`` + - ``bound_incoherent_scattering_cross_section`` + - ``total_bound_scattering_cross_section`` + - ``absorption_cross_section`` + """ + with _open_scattering_parameters_file() as f: + while line := f.readline(): + name, rest = line.split(',', 1) + if name == isotope: + return _parse_line(rest) + raise ValueError(f"No entry for element / isotope '{isotope}'") + + +def _parse_line(line: str) -> dict[str, Optional[sc.Variable]]: + line = line.rstrip().split(',') + return { + 'bound_coherent_scattering_length_re': _assemble_scalar(line[0], line[1], 'fm'), + 'bound_coherent_scattering_length_im': _assemble_scalar(line[2], line[3], 'fm'), + 'bound_incoherent_scattering_length_re': _assemble_scalar( + line[4], line[5], 'fm' + ), + 'bound_incoherent_scattering_length_im': _assemble_scalar( + line[6], line[7], 'fm' + ), + 'bound_coherent_scattering_cross_section': _assemble_scalar( + line[8], line[9], 'barn' + ), + 'bound_incoherent_scattering_cross_section': _assemble_scalar( + line[10], line[11], 'barn' + ), + 'total_bound_scattering_cross_section': _assemble_scalar( + line[12], line[13], 'barn' + ), + 'absorption_cross_section': _assemble_scalar(line[14], line[15], 'barn'), + } + + +def _assemble_scalar(value: str, std: str, unit: str) -> Optional[sc.Variable]: + if not value: + return None + value = float(value) + variance = float(std) ** 2 if std else None + return sc.scalar(value, variance=variance, unit=unit) diff --git a/src/scippneutron/atoms/scattering_parameters.csv b/src/scippneutron/atoms/scattering_parameters.csv new file mode 100644 index 000000000..455ed0248 --- /dev/null +++ b/src/scippneutron/atoms/scattering_parameters.csv @@ -0,0 +1,371 @@ +H,-3.739,,,,,,,,1.7568,,80.26,,82.02,,0.3326, +1H,-3.7406,,,,25.274,,,,1.7583,,80.27,,82.03,,0.3326, +2H,6.671,,,,4.04,,,,5.592,,2.05,,7.64,,0.000519, +3H,4.792,,,,-1.04,,,,2.89,,0.14,,3.03,,0.0, +He,3.26,3.0,,,,,,,1.34,,0.0,,1.34,,0.00747, +3He,5.74,,-1.483,,-2.5,,2.568,,4.42,,1.6,,6.0,,5333.0,7.0 +4He,3.26,,,,0.0,,,,1.34,,0.0,,1.34,,0.0, +Li,-1.9,,,,,,,,0.454,,0.92,,1.37,,70.5, +6Li,2.0,,-0.261,,-1.89,,0.26,,0.51,,0.46,,0.97,,940.0,4.0 +7Li,-2.22,,,,-2.49,,,,0.619,,0.78,,1.4,,0.0454, +Be,7.79,,,,0.12,,,,7.63,,0.0018,,7.63,,0.0076, +B,5.3,,-0.213,,,,,,3.54,,1.7,,5.24,,767.0,8.0 +10B,-0.1,,-1.066,,-4.7,,1.231,,0.144,,3.0,,3.1,,3835.0,9.0 +11B,6.65,,,,-1.3,,,,5.56,,0.21,,5.77,,0.0055, +C,6.646,,,,,,,,5.551,,0.001,,5.551,,0.0035, +12C,6.6511,,,,0.0,,,,5.559,,0.0,,5.559,,0.00353, +13C,6.19,,,,-0.52,,,,4.81,,0.034,,4.84,,0.00137, +N,9.36,,,,,,,,11.01,,0.5,,11.51,,1.9, +14N,9.37,,,,2.0,,,,11.03,,0.5,,11.53,,1.91, +15N,6.44,,,,-0.02,,,,5.21,,5e-05,,5.21,,2.4e-05, +O,5.803,,,,,,,,4.232,,0.0008,,4.232,,0.00019, +16O,5.803,,,,0.0,,,,4.232,,0.0,,4.232,,0.0001, +17O,5.78,,,,0.18,,,,4.2,,0.004,,4.2,,0.236, +18O,5.84,,,,0.0,,,,4.29,,0.0,,4.29,,0.00016, +F,5.654,,,,-0.082,,,,4.017,,0.0008,,4.018,,0.0096, +Ne,4.566,,,,,,,,2.62,,0.008,,2.628,,0.039, +20Ne,4.631,,,,0.0,,,,2.695,,0.0,,2.695,,0.036, +21Ne,6.66,,,,0.6,,,,5.6,,0.05,,5.7,,0.67, +22Ne,3.87,,,,0.0,,,,1.88,,0.0,,1.88,,0.046, +Na,3.63,,,,3.59,,,,1.66,,1.62,,3.28,,0.53, +Mg,5.375,,,,,,,,3.631,,0.08,,3.71,,0.063, +24Mg,5.66,,,,0.0,,,,4.03,,0.0,,4.03,,0.05, +25Mg,3.62,,,,1.48,,,,1.65,,0.28,,1.93,,0.19, +26Mg,4.89,,,,0.0,,,,3.0,,0.0,,3.0,,0.0382, +Al,3.449,,,,0.256,,,,1.495,,0.0082,,1.503,,0.231, +Si,4.1491,,,,,,,,2.163,,0.004,,2.167,,0.171, +28Si,4.107,,,,0.0,,,,2.12,,0.0,,2.12,,0.177, +29Si,4.7,,,,0.09,,,,2.78,,0.001,,2.78,,0.101, +30Si,4.58,,,,0.0,,,,2.64,,0.0,,2.64,,0.107, +P,5.13,,,,0.2,,,,3.307,,0.005,,3.312,,0.172, +S,2.847,,,,,,,,1.0186,,0.007,,1.026,,0.53, +32S,2.804,,,,0.0,,,,0.988,,0.0,,0.988,,0.54, +33S,4.74,,,,1.5,,,,2.8,,0.3,,3.1,,0.54, +34S,3.48,,,,0.0,,,,1.52,,0.0,,1.52,,0.227, +36S,3.0,1.0,,,0.0,,,,1.1,,0.0,,1.1,,0.15, +Cl,9.577,,,,,,,,11.5257,,5.3,,16.8,,33.5, +35Cl,11.65,,,,6.1,,,,17.06,,4.7,,21.8,,44.1, +37Cl,3.08,,,,0.1,,,,1.19,,0.001,,1.19,,0.433, +Ar,1.909,,,,,,,,0.458,,0.225,,0.683,,0.675, +36Ar,24.9,,,,0.0,,,,77.9,,0.0,,77.9,,5.2, +38Ar,3.5,,,,0.0,,,,1.5,3.1,0.0,,1.5,3.1,0.8, +40Ar,1.83,,,,0.0,,,,0.421,,0.0,,0.421,,0.66, +K,3.67,,,,,,,,1.69,,0.27,,1.96,,2.1, +39K,3.74,,,,1.4,,,,1.76,,0.25,,2.01,,2.1, +40K,3.0,1.0,,,,,,,1.1,,0.5,,1.6,,35.0,8.0 +41K,2.69,,,,1.5,,,,0.91,,0.3,,1.2,,1.46, +Ca,4.7,,,,,,,,2.78,,0.05,,2.83,,0.43, +40Ca,4.8,,,,0.0,,,,2.9,,0.0,,2.9,,0.41, +42Ca,3.36,,,,0.0,,,,1.42,,0.0,,1.42,,0.68, +43Ca,-1.56,,,,,,,,0.31,,0.5,,0.8,,6.2, +44Ca,1.42,,,,0.0,,,,0.25,,0.0,,0.25,,0.88, +46Ca,3.6,,,,0.0,,,,1.6,,0.0,,1.6,,0.74, +48Ca,0.39,,,,0.0,,,,0.019,,0.0,,0.019,,1.09, +Sc,12.29,,,,-6.0,,,,19.0,,4.5,,23.5,,27.5, +Ti,-3.438,,,,,,,,1.485,,2.87,,4.35,,6.09, +46Ti,4.93,,,,0.0,,,,3.05,,0.0,,3.05,,0.59, +47Ti,3.63,,,,-3.5,,,,1.66,,1.5,,3.2,,1.7, +48Ti,-6.08,,,,0.0,,,,4.65,,0.0,,4.65,,7.84, +49Ti,1.04,,,,5.1,,,,0.14,,3.3,,3.4,,2.2, +50Ti,6.18,,,,0.0,,,,4.8,,0.0,,4.8,,0.179, +V,-0.3824,,,,,,,,0.0184,,5.08,,5.1,,5.08, +50V,7.6,,,,,,,,7.3,1.1,0.5,,7.8,1.0,60.0,40.0 +51V,-0.402,,,,6.35,,,,0.0203,,5.07,,5.09,,4.9, +Cr,3.635,,,,,,,,1.66,,1.83,,3.49,,3.05, +50Cr,-4.5,,,,0.0,,,,2.54,,0.0,,2.54,,15.8, +52Cr,4.92,,,,0.0,,,,3.042,,0.0,,3.042,,0.76, +53Cr,-4.2,,,,6.87,,,,2.22,,5.93,,8.15,,18.1,1.5 +54Cr,4.55,,,,0.0,,,,2.6,,0.0,,2.6,,0.36, +Mn,-3.73,,,,1.79,,,,1.75,,0.4,,2.15,,13.3, +Fe,9.45,,,,,,,,11.22,,0.4,,11.62,,2.56, +54Fe,4.2,,,,0.0,,,,2.2,,0.0,,2.2,,2.25, +56Fe,9.94,,,,0.0,,,,12.42,,0.0,,12.42,,2.59, +57Fe,2.3,,,,,,,,0.66,,0.3,,1.0,,2.48, +58Fe,15.0,7.0,,,0.0,,,,28.0,,0.0,,28.0,26.0,1.28, +Co,2.49,,,,-6.2,,,,0.779,,4.8,,5.6,,37.18, +Ni,10.3,,,,,,,,13.3,,5.2,,18.5,,4.49, +58Ni,14.4,,,,0.0,,,,26.1,,0.0,,26.1,,4.6, +60Ni,2.8,,,,0.0,,,,0.99,,0.0,,0.99,,2.9, +61Ni,7.6,,,,3.9,,,,7.26,,1.9,,9.2,,2.5, +62Ni,-8.7,,,,0.0,,,,9.5,,0.0,,9.5,,14.5, +64Ni,-0.37,,,,0.0,,,,0.017,,0.0,,0.017,,1.52, +Cu,7.718,,,,,,,,7.485,,0.55,,8.03,,3.78, +63Cu,6.43,,,,0.22,,,,5.2,,0.006,,5.2,,4.5, +65Cu,10.61,,,,1.79,,,,14.1,,0.4,,14.5,,2.17, +Zn,5.68,,,,,,,,4.054,,0.077,,4.131,,1.11, +64Zn,5.22,,,,0.0,,,,3.42,,0.0,,3.42,,0.93, +66Zn,5.97,,,,0.0,,,,4.48,,0.0,,4.48,,0.62, +67Zn,7.56,,,,-1.5,,,,7.18,,0.28,,7.46,,6.8, +68Zn,6.03,,,,0.0,,,,4.57,,0.0,,4.57,,1.1, +70Zn,6.0,1.0,,,0.0,,,,4.5,,0.0,,4.5,1.5,0.092, +Ga,7.288,,,,,,,,6.675,,0.16,,6.83,,2.75, +69Ga,7.88,,,,-0.85,,,,7.8,,0.091,,7.89,,2.18, +71Ga,6.4,,,,-0.82,,,,5.15,,0.084,,5.23,,3.61, +Ge,8.185,,,,,,,,8.42,,0.18,,8.6,,2.2, +70Ge,10.0,,,,0.0,,,,12.6,,0.0,,12.6,,3.0, +72Ge,8.51,,,,0.0,,,,9.1,,0.0,,9.1,,0.8, +73Ge,5.02,,,,3.4,,,,3.17,,1.5,,4.7,,15.1, +74Ge,7.58,,,,0.0,,,,7.2,,0.0,,7.2,,0.4, +76Ge,8.2,,,,0.0,,,,8.0,3.0,0.0,,8.0,3.0,0.16, +As,6.58,,,,-0.69,,,,5.44,,0.06,,5.5,,4.5, +Se,7.97,,,,,,,,7.98,,0.32,,8.3,,11.7, +74Se,0.8,,,,0.0,,,,0.1,,0.0,,0.1,,51.8,1.2 +76Se,12.2,,,,0.0,,,,18.7,,0.0,,18.7,,85.0,7.0 +77Se,8.25,,,,0.6,1.6,,,8.6,,0.05,,8.65,,42.0,4.0 +78Se,8.24,,,,0.0,,,,8.5,,0.0,,8.5,,0.43, +80Se,7.48,,,,0.0,,,,7.03,,0.0,,7.03,,0.61, +82Se,6.34,,,,0.0,,,,5.05,,0.0,,5.05,,0.044, +Br,6.795,,,,,,,,5.8,,0.1,,5.9,,6.9, +79Br,6.8,,,,-1.1,,,,5.81,,0.15,,5.96,,11.0, +81Br,6.79,,,,0.6,,,,5.79,,0.05,,5.84,,2.7, +Kr,7.81,,,,,,,,7.67,,0.01,,7.68,,25.0,1.0 +78Kr,,,,,0.0,,,,,,0.0,,,,6.4, +80Kr,,,,,0.0,,,,,,0.0,,,,11.8, +82Kr,,,,,0.0,,,,,,0.0,,,,29.0,20.0 +83Kr,,,,,,,,,,,,,,,185.0,30.0 +84Kr,,,,,0.0,,,,,,0.0,,6.6,,0.113, +86Kr,8.1,,,,0.0,,,,8.2,,0.0,,8.2,,0.003, +Rb,7.09,,,,,,,,6.32,,0.5,,6.8,,0.38, +85Rb,7.03,,,,,,,,6.2,,0.5,,6.7,,0.48, +87Rb,7.23,,,,,,,,6.6,,0.5,,7.1,,0.12, +Sr,7.02,,,,,,,,6.19,,0.06,,6.25,,1.28, +84Sr,7.0,1.0,,,0.0,,,,6.0,2.0,0.0,,6.0,2.0,0.87, +86Sr,5.67,,,,0.0,,,,4.04,,0.0,,4.04,,1.04, +87Sr,7.4,,,,,,,,6.88,,0.5,,7.4,,16.0,3.0 +88Sr,7.15,,,,0.0,,,,6.42,,0.0,,6.42,,0.058, +Y,7.75,,,,1.1,,,,7.55,,0.15,,7.7,,1.28, +Zr,7.16,,,,,,,,6.44,,0.02,,6.46,,0.185, +90Zr,6.4,,,,0.0,,,,5.1,,0.0,,5.1,,0.011, +91Zr,8.7,,,,-1.08,,,,9.5,,0.15,,9.7,,1.17, +92Zr,7.4,,,,0.0,,,,6.9,,0.0,,6.9,,0.22, +94Zr,8.2,,,,0.0,,,,8.4,,0.0,,8.4,,0.0499, +96Zr,5.5,,,,0.0,,,,3.8,,0.0,,3.8,,0.0229, +Nb,7.054,,,,-0.139,,,,6.253,,0.0024,,6.255,,1.15, +Mo,6.715,,,,,,,,5.67,,0.04,,5.71,,2.48, +92Mo,6.91,,,,0.0,,,,6.0,,0.0,,6.0,,0.019, +94Mo,6.8,,,,0.0,,,,5.81,,0.0,,5.81,,0.015, +95Mo,6.91,,,,,,,,6.0,,0.5,,6.5,,13.1, +96Mo,6.2,,,,0.0,,,,4.83,,0.0,,4.83,,0.5, +97Mo,7.24,,,,,,,,6.59,,0.5,,7.1,,2.5, +98Mo,6.58,,,,0.0,,,,5.44,,0.0,,5.44,,0.127, +100Mo,6.73,,,,0.0,,,,5.69,,0.0,,5.69,,0.4, +Tc,6.8,,,,,,,,5.8,,0.5,,6.3,,20.0,1.0 +Ru,7.03,,,,,,,,6.21,,0.4,,6.6,,2.56, +96Ru,,,,,0.0,,,,,,0.0,,,,0.28, +98Ru,,,,,0.0,,,,,,0.0,,,,, +99Ru,,,,,,,,,,,,,,,6.9,1.0 +100Ru,,,,,0.0,,,,,,0.0,,,,4.8, +101Ru,,,,,,,,,,,,,,,3.3, +102Ru,,,,,0.0,,,,,,0.0,,144.8,,1.17, +104Ru,,,,,0.0,,,,,,0.0,,4.483,,0.31, +Rh,5.88,,,,,,,,4.34,,0.3,,4.6,,144.8, +Pd,5.91,,,,,,,,4.39,,0.093,,4.48,,6.9, +102Pd,7.7,7.0,,,0.0,,,,7.5,1.4,0.0,,7.5,1.4,3.4, +104Pd,7.7,7.0,,,0.0,,,,7.5,1.4,0.0,,7.5,1.4,0.6, +105Pd,5.5,,,,-2.6,1.6,,,3.8,,0.8,,4.6,1.1,20.0,3.0 +106Pd,6.4,,,,0.0,,,,5.1,,0.0,,5.1,,0.304, +108Pd,4.1,,,,0.0,,,,2.1,,0.0,,2.1,,8.55, +110Pd,7.7,7.0,,,0.0,,,,7.5,1.4,0.0,,7.5,1.4,0.226, +Ag,5.922,,,,,,,,4.407,,0.58,,4.99,,63.3, +107Ag,7.555,,,,1.0,,,,7.17,,0.13,,7.3,,37.6,1.2 +109Ag,4.165,,,,-1.6,,,,2.18,,0.32,,2.5,,91.0,1.0 +Cd,4.87,,-0.7,,,,,,3.04,,3.46,,6.5,,2520.0,50.0 +106Cd,5.0,2.0,,,0.0,,,,3.1,,0.0,,3.1,2.5,1.0, +108Cd,5.4,,,,0.0,,,,3.7,,0.0,,3.7,,1.1, +110Cd,5.9,,,,0.0,,,,4.4,,0.0,,4.4,,11.0, +111Cd,6.5,,,,,,,,5.3,,0.3,,5.6,,24.0, +112Cd,6.4,,,,0.0,,,,5.1,,0.0,,5.1,,2.2, +113Cd,-8.0,,-5.73,,,,,,12.1,,0.3,,12.4,,20600.0,400.0 +114Cd,7.5,,,,0.0,,,,7.1,,0.0,,7.1,,0.34, +116Cd,6.3,,,,0.0,,,,5.0,,0.0,,5.0,,0.075, +In,4.065,,-0.0539,,,,,,2.08,,0.54,,2.62,,193.8,1.5 +113In,5.39,,,,0.017,,,,3.65,,3.7e-05,,3.65,,12.0,1.1 +115In,4.01,,-0.0562,,-2.1,,,,2.02,,0.55,,2.57,,202.0,2.0 +Sn,6.225,,,,,,,,4.871,,0.022,,4.892,,0.626, +112Sn,6.0,1.0,,,0.0,,,,4.5,1.5,0.0,,4.5,1.5,1.0, +114Sn,6.2,,,,0.0,,,,4.8,,0.0,,4.8,,0.114, +115Sn,6.0,1.0,,,,,,,4.5,1.5,0.3,,4.8,1.5,30.0,7.0 +116Sn,5.93,,,,0.0,,,,4.42,,0.0,,4.42,,0.14, +117Sn,6.48,,,,,,,,5.28,,0.3,,5.6,,2.3, +118Sn,6.07,,,,0.0,,,,4.63,,0.0,,4.63,,0.22, +119Sn,6.12,,,,,,,,4.71,,0.3,,5.0,,2.2, +120Sn,6.49,,,,0.0,,,,5.29,,0.0,,5.29,,0.14, +122Sn,5.74,,,,0.0,,,,4.14,,0.0,,4.14,,0.18, +124Sn,5.97,,,,0.0,,,,4.48,,0.0,,4.48,,0.133, +Sb,5.57,,,,,,,,3.9,,0.007,,3.9,,4.91, +121Sb,5.71,,,,-0.05,,,,4.1,,0.0003,,4.1,,5.75, +123Sb,5.38,,,,-0.1,,,,3.64,,0.001,,3.64,,3.8, +Te,5.8,,,,,,,,4.23,,0.09,,4.32,,4.7, +120Te,5.3,,,,0.0,,,,3.5,,0.0,,3.5,,2.3, +122Te,3.8,,,,0.0,,,,1.8,,0.0,,1.8,,3.4, +123Te,-0.05,,-0.116,,-2.04,,,,0.002,,0.52,,0.52,,418.0,30.0 +124Te,7.96,,,,0.0,,,,8.0,,0.0,,8.0,,6.8,1.3 +125Te,5.02,,,,-0.26,,,,3.17,,0.008,,3.18,,1.55, +126Te,5.56,,,,0.0,,,,3.88,,0.0,,3.88,,1.04, +128Te,5.89,,,,0.0,,,,4.36,,0.0,,4.36,,0.215, +130Te,6.02,,,,0.0,,,,4.55,,0.0,,4.55,,0.29, +I,5.28,,,,1.58,,,,3.5,,0.31,,3.81,,6.15, +Xe,4.92,,,,3.04,,,,2.96,,0.0,,,,23.9,1.2 +124Xe,,,,,0.0,,,,,,0.0,,,,165.0,20.0 +126Xe,,,,,0.0,,,,,,0.0,,,,3.5, +128Xe,,,,,0.0,,,,,,0.0,,,,, +129Xe,,,,,,,,,,,,,,,21.0,5.0 +130Xe,,,,,0.0,,,,,,0.0,,,,, +131Xe,,,,,,,,,,,,,,,85.0,10.0 +132Xe,,,,,0.0,,,,,,0.0,,,,0.45, +134Xe,,,,,0.0,,,,,,0.0,,,,0.265, +136Xe,,,,,0.0,,,,,,0.0,,,,0.26, +Cs,5.42,,,,1.29,,,,3.69,,0.21,,3.9,,29.0,1.5 +Ba,5.07,,,,,,,,3.23,,0.15,,3.38,,1.1, +130Ba,-3.6,,,,0.0,,,,1.6,,0.0,,1.6,,30.0,5.0 +132Ba,7.8,,,,0.0,,,,7.6,,0.0,,7.6,,7.0, +134Ba,5.7,,,,0.0,,,,4.08,,0.0,,4.08,,2.0,1.6 +135Ba,4.67,,,,,,,,2.74,,0.5,,3.2,,5.8, +136Ba,4.91,,,,0.0,,,,3.03,,0.0,,3.03,,0.68, +137Ba,6.83,,,,,,,,5.86,,0.5,,6.4,,3.6, +138Ba,4.84,,,,0.0,,,,2.94,,0.0,,2.94,,0.27, +La,8.24,,,,,,,,8.53,,1.13,,9.66,,8.97, +138La,8.0,2.0,,,,,,,8.0,4.0,0.5,,8.5,4.0,57.0,6.0 +139La,8.24,,,,3.0,,,,8.53,,1.13,,9.66,,8.93, +Ce,4.84,,,,,,,,2.94,,0.001,,2.94,,0.63, +136Ce,5.8,,,,0.0,,,,4.23,,0.0,,4.23,,7.3,1.5 +138Ce,6.7,,,,0.0,,,,5.64,,0.0,,5.64,,1.1, +140Ce,4.84,,,,0.0,,,,2.94,,0.0,,2.94,,0.57, +142Ce,4.75,,,,0.0,,,,2.84,,0.0,,2.84,,0.95, +Pr,4.58,,,,-0.35,,,,2.64,,0.015,,2.66,,11.5, +Nd,7.69,,,,,,,,7.43,,9.2,,16.6,,50.5,1.2 +142Nd,7.7,,,,0.0,,,,7.5,,0.0,,7.5,,18.7, +143Nd,14.0,2.0,,,21.0,1.0,,,25.0,7.0,55.0,7.0,80.0,2.0,337.0,10.0 +144Nd,2.8,,,,0.0,,,,1.0,,0.0,,1.0,,3.6, +145Nd,14.0,2.0,,,,,,,25.0,7.0,5.0,5.0,30.0,9.0,42.0,2.0 +146Nd,8.7,,,,0.0,,,,9.5,,0.0,,9.5,,1.4, +148Nd,5.7,,,,0.0,,,,4.1,,0.0,,4.1,,2.5, +150Nd,5.3,,,,0.0,,,,3.5,,0.0,,3.5,,1.2, +Pm,12.6,,,,3.2,2.5,,,20.0,1.3,1.3,2.0,21.3,1.5,168.4,3.5 +Sm,0.8,,-1.65,,,,,,0.422,,39.0,3.0,39.0,3.0,5922.0,56.0 +144Sm,-3.0,4.0,,,0.0,,,,1.0,3.0,0.0,,1.0,3.0,0.7, +147Sm,14.0,3.0,,,11.0,7.0,,,25.0,11.0,143.0,19.0,39.0,16.0,57.0,3.0 +148Sm,-3.0,4.0,,,0.0,,,,1.0,3.0,0.0,,1.0,3.0,2.4, +149Sm,-19.2,,-11.7,,31.4,,-10.3,,63.5,,137.0,5.0,200.0,5.0,42080.0,400.0 +150Sm,14.0,3.0,,,0.0,,,,25.0,11.0,0.0,,25.0,11.0,104.0,4.0 +152Sm,-5.0,,,,0.0,,,,3.1,,0.0,,3.1,,206.0,6.0 +154Sm,9.3,,,,0.0,,,,11.0,2.0,0.0,,11.0,2.0,8.4, +Eu,7.22,,-1.26,,,,,,6.57,,2.5,,9.2,,4530.0,40.0 +151Eu,6.13,,-2.53,,4.5,,-2.14,,5.5,,3.1,,8.6,,9100.0,100.0 +153Eu,8.22,,,,3.2,,,,8.5,,1.3,,9.8,,312.0,7.0 +Gd,6.5,,-13.82,,,,,,29.3,,151.0,2.0,180.0,2.0,49700.0,125.0 +152Gd,10.0,3.0,,,0.0,,,,13.0,8.0,0.0,,13.0,8.0,735.0,20.0 +154Gd,10.0,3.0,,,0.0,,,,13.0,8.0,0.0,,13.0,8.0,85.0,12.0 +155Gd,6.0,,-17.0,,5.0,5.0,-13.16,,40.8,,25.0,6.0,66.0,6.0,61100.0,400.0 +156Gd,6.3,,,,0.0,,,,5.0,,0.0,,5.0,,1.5,1.2 +157Gd,-1.14,,-71.9,,5.0,5.0,-55.8,,650.0,4.0,394.0,7.0,1044.0,8.0,259000.0,700.0 +158Gd,9.0,2.0,,,0.0,,,,10.0,5.0,0.0,,10.0,5.0,2.2, +160Gd,9.15,,,,0.0,,,,10.52,,0.0,,10.52,,0.77, +Tb,7.38,,,,-0.17,,,,6.84,,0.004,,6.84,,23.4, +Dy,16.9,,-0.276,,,,,,35.9,,54.4,1.2,90.3,,994.0,13.0 +156Dy,6.1,,,,0.0,,,,4.7,,0.0,,4.7,,33.0,3.0 +158Dy,6.0,4.0,,,0.0,,,,5.0,6.0,0.0,,5.0,6.0,43.0,6.0 +160Dy,6.7,,,,0.0,,,,5.6,,0.0,,5.6,,56.0,5.0 +161Dy,10.3,,,,4.9,,,,13.3,,3.0,1.0,16.0,1.0,600.0,25.0 +162Dy,-1.4,,,,0.0,,,,0.25,,0.0,,0.25,,194.0,10.0 +163Dy,5.0,,,,1.3,,,,3.1,,0.21,,3.3,,124.0,7.0 +164Dy,49.4,,-0.79,,0.0,,,,307.0,3.0,0.0,,307.0,3.0,2840.0,40.0 +Ho,8.01,,,,-1.7,,,,8.06,,0.36,,8.42,,64.7,1.2 +Er,7.79,,,,,,,,7.63,,1.1,,8.7,,159.0,4.0 +162Er,8.8,,,,0.0,,,,9.7,,0.0,,9.7,,19.0,2.0 +164Er,8.2,,,,0.0,,,,8.4,,0.0,,8.4,,13.0,2.0 +166Er,10.6,,,,0.0,,,,14.1,,0.0,,14.1,,19.6,1.5 +167Er,3.0,,,,1.0,,,,1.1,,0.13,,1.2,,659.0,16.0 +168Er,7.4,,,,0.0,,,,6.9,,0.0,,6.9,,2.74, +170Er,9.6,,,,0.0,,,,11.6,,0.0,,11.6,1.2,5.8, +Tm,7.07,,,,0.9,,,,6.28,,0.1,,6.38,,100.0,2.0 +Yb,12.43,,,,,,,,19.42,,4.0,,23.4,,34.8, +168Yb,-4.07,,-0.62,,0.0,,,,2.13,,0.0,,2.13,,2230.0,40.0 +170Yb,6.77,,,,0.0,,,,5.8,,0.0,,5.8,,11.4,1.0 +171Yb,9.66,,,,-5.59,,,,11.7,,3.9,,15.6,,48.6,2.5 +172Yb,9.43,,,,0.0,,,,11.2,,0.0,,11.2,,0.8, +173Yb,9.56,,,,-5.3,,,,11.5,,3.5,,15.0,,17.1,1.3 +174Yb,19.3,,,,0.0,,,,46.8,,0.0,,46.8,,69.4,5.0 +176Yb,8.72,,,,0.0,,,,9.6,,0.0,,9.6,,2.85, +Lu,7.21,,,,,,,,6.53,,0.7,,7.2,,74.0,2.0 +175Lu,7.24,,,,2.2,,,,6.59,,0.6,,7.2,,21.0,3.0 +176Lu,6.1,,-0.57,,3.0,,0.61,,4.7,,1.2,,5.9,,2065.0,35.0 +Hf,7.7,,,,,,,,7.6,,2.6,,10.2,,104.1, +174Hf,10.9,1.1,,,0.0,,,,15.0,3.0,0.0,,15.0,3.0,561.0,35.0 +176Hf,6.61,,,,0.0,,,,5.5,,0.0,,5.5,,23.5,3.1 +177Hf,0.8,1.0,,,0.9,1.3,,,0.1,,0.1,,0.2,,373.0,10.0 +178Hf,5.9,,,,0.0,,,,4.4,,0.0,,4.4,,84.0,4.0 +179Hf,7.46,,,,1.06,,,,7.0,,0.14,,7.1,,41.0,3.0 +180Hf,13.2,,,,0.0,,,,21.9,,0.0,,21.9,1.0,13.04, +Ta,6.91,,,,,,,,6.0,,0.01,,6.01,,20.6, +180Ta,7.0,2.0,,,,,,,6.2,,0.5,,7.0,4.0,563.0,60.0 +181Ta,6.91,,,,-0.29,,,,6.0,,0.011,,6.01,,20.5, +W,4.86,,,,,,,,2.97,,1.63,,4.6,,18.3, +180W,5.0,3.0,,,0.0,,,,3.0,4.0,0.0,,3.0,4.0,30.0,20.0 +182W,6.97,,,,0.0,,,,6.1,,0.0,,6.1,,20.7, +183W,6.53,,,,,,,,5.36,,0.3,,5.7,,10.1, +184W,7.48,,,,0.0,,,,7.03,,0.0,,7.03,,1.7, +186W,-0.72,,,,0.0,,,,0.065,,0.0,,0.065,,37.9, +Re,9.2,,,,,,,,10.6,,0.9,,11.5,,89.7,1.0 +185Re,9.0,,,,2.0,,,,10.2,,0.5,,10.7,,112.0,2.0 +187Re,9.3,,,,2.8,,,,10.9,,1.0,,11.9,,76.4,1.0 +Os,10.7,,,,,,,,14.4,,0.3,,14.7,,16.0, +184Os,10.0,2.0,,,0.0,,,,13.0,5.0,0.0,,13.0,5.0,3000.0,150.0 +186Os,11.6,1.7,,,0.0,,,,17.0,5.0,0.0,,17.0,5.0,80.0,13.0 +187Os,10.0,2.0,,,,,,,13.0,5.0,0.3,,13.0,5.0,320.0,10.0 +188Os,7.6,,,,0.0,,,,7.3,,0.0,,7.3,,4.7, +189Os,10.7,,,,,,,,14.4,,0.5,,14.9,,25.0,4.0 +190Os,11.0,,,,0.0,,,,15.2,,0.0,,15.2,,13.1, +192Os,11.5,,,,0.0,,,,16.6,,0.0,,16.6,1.2,2.0, +Ir,10.6,,,,,,,,14.1,,0.0,3.0,14.0,3.0,425.0,2.0 +191Ir,,,,,,,,,,,,,,,954.0,10.0 +193Ir,,,,,,,,,,,,,,,111.0,5.0 +Pt,9.6,,,,,,,,11.58,,0.13,,11.71,,10.3, +190Pt,9.0,,,,0.0,,,,10.0,2.0,0.0,,10.0,2.0,152.0,4.0 +192Pt,9.9,,,,0.0,,,,12.3,1.2,0.0,,12.3,1.2,10.0,2.5 +194Pt,10.55,,,,0.0,,,,14.0,,0.0,,14.0,,1.44, +195Pt,8.83,,,,-1.0,,,,9.8,,0.13,,9.9,,27.5,1.2 +196Pt,9.89,,,,0.0,,,,12.3,,0.0,,12.3,,0.72, +198Pt,7.8,,,,0.0,,,,7.6,,0.0,,7.6,,3.66, +Au,7.63,,,,-1.84,,,,7.32,,0.43,,7.75,,98.65, +Hg,12.692,,,,,,,,20.24,,6.6,,26.8,,372.3,4.0 +196Hg,30.3,1.0,,,0.0,,,,115.0,8.0,0.0,,115.0,8.0,3080.0,180.0 +198Hg,,,,,0.0,,,,,,0.0,,,,2.0, +199Hg,16.9,,,,15.5,,,,36.0,2.0,30.0,3.0,66.0,2.0,2150.0,48.0 +200Hg,,,,,0.0,,,,,,0.0,,,,, +201Hg,,,,,,,,,,,,,,,7.8,2.0 +202Hg,,,,,0.0,,,,,,0.0,,9.828,,4.89, +204Hg,,,,,0.0,,,,,,0.0,,,,0.43, +Tl,8.776,,,,,,,,9.678,,0.21,,9.89,,3.43, +203Tl,6.99,,,,1.06,,,,6.14,,0.14,,6.28,,11.4, +205Tl,9.52,,,,-0.242,,,,11.39,,0.007,,11.4,,0.104, +Pb,9.405,,,,,,,,11.115,,0.003,,11.118,,0.171, +204Pb,9.9,,,,0.0,,,,12.3,,0.0,,12.3,,0.65, +206Pb,9.22,,,,0.0,,,,10.68,,0.0,,10.68,,0.03, +207Pb,9.28,,,,0.14,,,,10.82,,0.002,,10.82,,0.699, +208Pb,9.5,,,,0.0,,,,11.34,,0.0,,11.34,,0.00048, +Bi,8.532,,,,,,,,9.148,,0.0084,,9.156,,0.0338, +Po,,,,,0.259,,,,0.0,,,,,,, +At,,,,,,,,,0.0,,,,,,, +Rn,,,,,,,,,0.0,,,,12.6,,, +Fr,,,,,,,,,0.0,,,,,,, +Ra,10.0,1.0,,,0.0,,,,13.0,3.0,0.0,,13.0,3.0,12.8,1.5 +Ac,,,,,,,,,0.0,,,,,,, +Th,10.31,,,,0.0,,,,13.36,,0.0,,13.36,,7.37, +Pa,9.1,,,,,,,,10.4,,0.1,3.3,10.5,3.2,200.6,2.3 +U,8.417,,,,,,,,8.903,,0.005,,8.908,,7.57, +233U,10.1,,,,1.0,3.0,,,12.8,,0.1,,12.9,,574.7,1.0 +234U,12.4,,,,0.0,,,,19.3,,0.0,,19.3,,100.1,1.3 +235U,10.47,,,,1.3,,,,13.78,,0.2,,14.0,,680.9,1.1 +238U,8.402,,,,0.0,,,,8.871,,0.0,,8.871,,2.68, +Np,10.55,,,,,,,,14.0,,0.5,,14.5,,175.9,2.9 +Pu,,,,,,,,,,,,,,,, +238Pu,14.1,,,,0.0,,,,25.0,1.8,0.0,,25.0,1.8,558.0,7.0 +239Pu,7.7,,,,1.3,1.9,,,7.5,,0.2,,7.7,,1017.3,2.1 +240Pu,3.5,,,,0.0,,,,1.54,,0.0,,1.54,,289.6,1.4 +242Pu,8.1,,,,0.0,,,,8.2,,0.0,,8.2,,18.5, +Am,8.3,,,,2.0,7.0,,,8.7,,0.3,,9.0,2.6,75.3,1.8 +Cm,,,,,,,,,0.0,,,,,,, +244Cm,9.5,,,,0.0,,,,11.3,,0.0,,11.3,,16.2,1.2 +246Cm,9.3,,,,0.0,,,,10.9,,0.0,,10.9,,1.36, +248Cm,7.7,,,,0.0,,,,7.5,,0.0,,7.5,,3.0, diff --git a/tests/atoms/test_atoms.py b/tests/atoms/test_atoms.py new file mode 100644 index 000000000..0d71b94d3 --- /dev/null +++ b/tests/atoms/test_atoms.py @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright (c) 2023 Scipp contributors (https://github.com/scipp) + +import pytest +import scipp as sc +from scipp.testing import assert_identical + +import scippneutron as scn + + +def test_scattering_params_h(): + params = scn.atoms.scattering_params('H') + assert len(params) == 8 + assert_identical( + params['bound_coherent_scattering_length_re'], sc.scalar(-3.739, unit='fm') + ) + assert params['bound_coherent_scattering_length_im'] is None + assert params['bound_incoherent_scattering_length_re'] is None + assert params['bound_incoherent_scattering_length_im'] is None + assert_identical( + params['bound_coherent_scattering_cross_section'], + sc.scalar(1.7568, unit='barn'), + ) + assert_identical( + params['bound_incoherent_scattering_cross_section'], + sc.scalar(80.26, unit='barn'), + ) + assert_identical( + params['total_bound_scattering_cross_section'], sc.scalar(82.02, unit='barn') + ) + assert_identical(params['absorption_cross_section'], sc.scalar(0.3326, unit='barn')) + + +def test_scattering_params_157gd(): + params = scn.atoms.scattering_params('157Gd') + assert len(params) == 8 + assert_identical( + params['bound_coherent_scattering_length_re'], sc.scalar(-1.14, unit='fm') + ) + assert_identical( + params['bound_coherent_scattering_length_im'], sc.scalar(-71.9, unit='fm') + ) + assert_identical( + params['bound_incoherent_scattering_length_re'], + sc.scalar(5.0, variance=5.0**2, unit='fm'), + ) + assert_identical( + params['bound_incoherent_scattering_length_im'], sc.scalar(-55.8, unit='fm') + ) + assert_identical( + params['bound_coherent_scattering_cross_section'], + sc.scalar(650.0, variance=4.0**2, unit='barn'), + ) + assert_identical( + params['bound_incoherent_scattering_cross_section'], + sc.scalar(394.0, variance=7.0**2, unit='barn'), + ) + assert_identical( + params['total_bound_scattering_cross_section'], + sc.scalar(1044.0, variance=8.0**2, unit='barn'), + ) + assert_identical( + params['absorption_cross_section'], + sc.scalar(259000.0, variance=700.0**2, unit='barn'), + ) + + +def test_scattering_params_unknown(): + with pytest.raises(ValueError): + scn.atoms.scattering_params('scippium') From c760ee3e79abf35de92068c6aa80df1294f208db Mon Sep 17 00:00:00 2001 From: Jan-Lukas Wynen Date: Tue, 20 Feb 2024 08:47:55 +0100 Subject: [PATCH 2/5] Remove redundant fields --- docs/bibliography.bib | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/bibliography.bib b/docs/bibliography.bib index 8c1b9a8e4..34db98151 100644 --- a/docs/bibliography.bib +++ b/docs/bibliography.bib @@ -31,6 +31,4 @@ @article{sears:1992 year = {1992}, publisher = {Taylor & Francis}, doi = {10.1080/10448639208218770}, - URL = {https://doi.org/10.1080/10448639208218770}, - eprint = {https://doi.org/10.1080/10448639208218770} } From 33ab3ad635d6bcf3c128675a2cc94030f0a57b3a Mon Sep 17 00:00:00 2001 From: Jan-Lukas Wynen Date: Tue, 20 Feb 2024 08:52:32 +0100 Subject: [PATCH 3/5] Fix layout --- src/scippneutron/atoms/__init__.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/scippneutron/atoms/__init__.py b/src/scippneutron/atoms/__init__.py index 7df522d01..cae07ac74 100644 --- a/src/scippneutron/atoms/__init__.py +++ b/src/scippneutron/atoms/__init__.py @@ -42,14 +42,14 @@ def scattering_params(isotope: str) -> dict[str, Optional[sc.Variable]]: Keys: - - ``bound_coherent_scattering_length_re`` - - ``bound_coherent_scattering_length_im`` - - ``bound_incoherent_scattering_length_re`` - - ``bound_incoherent_scattering_length_im`` - - ``bound_coherent_scattering_cross_section`` - - ``bound_incoherent_scattering_cross_section`` - - ``total_bound_scattering_cross_section`` - - ``absorption_cross_section`` + - ``bound_coherent_scattering_length_re`` + - ``bound_coherent_scattering_length_im`` + - ``bound_incoherent_scattering_length_re`` + - ``bound_incoherent_scattering_length_im`` + - ``bound_coherent_scattering_cross_section`` + - ``bound_incoherent_scattering_cross_section`` + - ``total_bound_scattering_cross_section`` + - ``absorption_cross_section`` """ with _open_scattering_parameters_file() as f: while line := f.readline(): From 7d02b44bc5ed0607426345379eb28d671e849c8d Mon Sep 17 00:00:00 2001 From: Jan-Lukas Wynen Date: Tue, 20 Feb 2024 09:05:45 +0100 Subject: [PATCH 4/5] Provide reference wavelength --- src/scippneutron/atoms/__init__.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/scippneutron/atoms/__init__.py b/src/scippneutron/atoms/__init__.py index cae07ac74..86f860814 100644 --- a/src/scippneutron/atoms/__init__.py +++ b/src/scippneutron/atoms/__init__.py @@ -9,12 +9,15 @@ import scipp as sc -def _open_scattering_parameters_file() -> TextIO: - return ( - importlib.resources.files('scippneutron.atoms') - .joinpath('scattering_parameters.csv') - .open('r') - ) +def reference_wavelength() -> sc.Variable: + """Return the reference wavelength for absorption cross-sections. + + Returns + ------- + : + 1.7982 Å + """ + return sc.scalar(1.7982, unit='angstrom') @lru_cache() @@ -27,6 +30,10 @@ def scattering_params(isotope: str) -> dict[str, Optional[sc.Variable]]: https://www.ncnr.nist.gov/resources/n-lengths/list.html which is based on :cite:`sears:1992`. + The absorption cross-section applies to neutrons with a wavelength + of 1.7982 Å. + See :func:`reference_wavelength`. + Parameters ---------- isotope: @@ -59,6 +66,14 @@ def scattering_params(isotope: str) -> dict[str, Optional[sc.Variable]]: raise ValueError(f"No entry for element / isotope '{isotope}'") +def _open_scattering_parameters_file() -> TextIO: + return ( + importlib.resources.files('scippneutron.atoms') + .joinpath('scattering_parameters.csv') + .open('r') + ) + + def _parse_line(line: str) -> dict[str, Optional[sc.Variable]]: line = line.rstrip().split(',') return { From 48faefa5c4a9b99cfd18dd152314e3f62497c1fd Mon Sep 17 00:00:00 2001 From: Jan-Lukas Wynen Date: Tue, 20 Feb 2024 10:41:45 +0100 Subject: [PATCH 5/5] Use dataclass for ScatteringParams --- src/scippneutron/atoms/__init__.py | 130 +++++++++++++++++------------ tests/atoms/test_atoms.py | 84 +++++++------------ 2 files changed, 109 insertions(+), 105 deletions(-) diff --git a/src/scippneutron/atoms/__init__.py b/src/scippneutron/atoms/__init__.py index 86f860814..154081904 100644 --- a/src/scippneutron/atoms/__init__.py +++ b/src/scippneutron/atoms/__init__.py @@ -1,10 +1,12 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2023 Scipp contributors (https://github.com/scipp) """Parameters for neutron interactions with atoms.""" +from __future__ import annotations +import dataclasses import importlib.resources from functools import lru_cache -from typing import Optional, TextIO +from typing import Optional, TextIO, Union import scipp as sc @@ -20,50 +22,73 @@ def reference_wavelength() -> sc.Variable: return sc.scalar(1.7982, unit='angstrom') -@lru_cache() -def scattering_params(isotope: str) -> dict[str, Optional[sc.Variable]]: - """Return the scattering parameters for the given element / isotope. +@dataclasses.dataclass(frozen=True, eq=False) +class ScatteringParams: + """Scattering parameters for neutrons with a specific element / isotope. Provides access to the scattering lengths and cross-sections of neutrons with a given element or isotope. Values have been retrieved at 2024-02-19T17:00:00Z from the list at https://www.ncnr.nist.gov/resources/n-lengths/list.html which is based on :cite:`sears:1992`. + Values are ``None`` where the table does not provide values. The absorption cross-section applies to neutrons with a wavelength of 1.7982 Å. See :func:`reference_wavelength`. - - Parameters - ---------- - isotope: - Name of the element or isotope. - For example, 'H', '3He', 'V', '50V'. - - Returns - ------- - : - Dict with the scattering parameters. - A value can be ``None`` if the parameter is not available. - Scattering lengths are split into real and imaginary parts. - - Keys: - - - ``bound_coherent_scattering_length_re`` - - ``bound_coherent_scattering_length_im`` - - ``bound_incoherent_scattering_length_re`` - - ``bound_incoherent_scattering_length_im`` - - ``bound_coherent_scattering_cross_section`` - - ``bound_incoherent_scattering_cross_section`` - - ``total_bound_scattering_cross_section`` - - ``absorption_cross_section`` """ - with _open_scattering_parameters_file() as f: - while line := f.readline(): - name, rest = line.split(',', 1) - if name == isotope: - return _parse_line(rest) - raise ValueError(f"No entry for element / isotope '{isotope}'") + + isotope: str + """Element / isotope name.""" + coherent_scattering_length_re: Optional[sc.Variable] + """Bound coherent scattering length (real part).""" + coherent_scattering_length_im: Optional[sc.Variable] + """Bound coherent scattering length (imaginary part).""" + incoherent_scattering_length_re: Optional[sc.Variable] + """Bound incoherent scattering length (real part).""" + incoherent_scattering_length_im: Optional[sc.Variable] + """Bound incoherent scattering length (imaginary part).""" + coherent_scattering_cross_section: Optional[sc.Variable] + """Bound coherent scattering cross-section.""" + incoherent_scattering_cross_section: Optional[sc.Variable] + """Bound incoherent scattering cross-section.""" + total_scattering_cross_section: Optional[sc.Variable] + """Total bound scattering cross-section.""" + absorption_cross_section: Optional[sc.Variable] + """Absorption cross-section for λ = 1.7982 Å neutrons.""" + + def __eq__(self, other: object) -> Union[bool, type(NotImplemented)]: + if not isinstance(other, ScatteringParams): + return NotImplemented + return all( + self.isotope == other.isotope + if field.name == 'isotope' + else _eq_or_identical(getattr(self, field.name), getattr(other, field.name)) + for field in dataclasses.fields(self) + ) + + @staticmethod + @lru_cache() + def for_isotope(isotope: str) -> ScatteringParams: + """Return the scattering parameters for the given element / isotope. + + Parameters + ---------- + isotope: + Name of the element or isotope. + For example, 'H', '3He', 'V', '50V'. + + Returns + ------- + : + Neutron scattering parameters. + """ + with _open_scattering_parameters_file() as f: + while line := f.readline(): + name, rest = line.split(',', 1) + if name == isotope: + return _parse_line(isotope, rest) + raise ValueError(f"No entry for element / isotope '{isotope}'") def _open_scattering_parameters_file() -> TextIO: @@ -74,28 +99,21 @@ def _open_scattering_parameters_file() -> TextIO: ) -def _parse_line(line: str) -> dict[str, Optional[sc.Variable]]: +def _parse_line(isotope: str, line: str) -> ScatteringParams: line = line.rstrip().split(',') - return { - 'bound_coherent_scattering_length_re': _assemble_scalar(line[0], line[1], 'fm'), - 'bound_coherent_scattering_length_im': _assemble_scalar(line[2], line[3], 'fm'), - 'bound_incoherent_scattering_length_re': _assemble_scalar( - line[4], line[5], 'fm' - ), - 'bound_incoherent_scattering_length_im': _assemble_scalar( - line[6], line[7], 'fm' - ), - 'bound_coherent_scattering_cross_section': _assemble_scalar( - line[8], line[9], 'barn' - ), - 'bound_incoherent_scattering_cross_section': _assemble_scalar( + return ScatteringParams( + isotope=isotope, + coherent_scattering_length_re=_assemble_scalar(line[0], line[1], 'fm'), + coherent_scattering_length_im=_assemble_scalar(line[2], line[3], 'fm'), + incoherent_scattering_length_re=_assemble_scalar(line[4], line[5], 'fm'), + incoherent_scattering_length_im=_assemble_scalar(line[6], line[7], 'fm'), + coherent_scattering_cross_section=_assemble_scalar(line[8], line[9], 'barn'), + incoherent_scattering_cross_section=_assemble_scalar( line[10], line[11], 'barn' ), - 'total_bound_scattering_cross_section': _assemble_scalar( - line[12], line[13], 'barn' - ), - 'absorption_cross_section': _assemble_scalar(line[14], line[15], 'barn'), - } + total_scattering_cross_section=_assemble_scalar(line[12], line[13], 'barn'), + absorption_cross_section=_assemble_scalar(line[14], line[15], 'barn'), + ) def _assemble_scalar(value: str, std: str, unit: str) -> Optional[sc.Variable]: @@ -104,3 +122,9 @@ def _assemble_scalar(value: str, std: str, unit: str) -> Optional[sc.Variable]: value = float(value) variance = float(std) ** 2 if std else None return sc.scalar(value, variance=variance, unit=unit) + + +def _eq_or_identical(a: Optional[sc.Variable], b: Optional[sc.Variable]) -> bool: + if a is None: + return b is None + return sc.identical(a, b) diff --git a/tests/atoms/test_atoms.py b/tests/atoms/test_atoms.py index 0d71b94d3..bd9153dfb 100644 --- a/tests/atoms/test_atoms.py +++ b/tests/atoms/test_atoms.py @@ -3,68 +3,48 @@ import pytest import scipp as sc -from scipp.testing import assert_identical import scippneutron as scn def test_scattering_params_h(): - params = scn.atoms.scattering_params('H') - assert len(params) == 8 - assert_identical( - params['bound_coherent_scattering_length_re'], sc.scalar(-3.739, unit='fm') + params = scn.atoms.ScatteringParams.for_isotope('H') + expected = scn.atoms.ScatteringParams( + isotope='H', + coherent_scattering_length_re=sc.scalar(-3.739, unit='fm'), + coherent_scattering_length_im=None, + incoherent_scattering_length_re=None, + incoherent_scattering_length_im=None, + coherent_scattering_cross_section=sc.scalar(1.7568, unit='barn'), + incoherent_scattering_cross_section=sc.scalar(80.26, unit='barn'), + total_scattering_cross_section=sc.scalar(82.02, unit='barn'), + absorption_cross_section=sc.scalar(0.3326, unit='barn'), ) - assert params['bound_coherent_scattering_length_im'] is None - assert params['bound_incoherent_scattering_length_re'] is None - assert params['bound_incoherent_scattering_length_im'] is None - assert_identical( - params['bound_coherent_scattering_cross_section'], - sc.scalar(1.7568, unit='barn'), - ) - assert_identical( - params['bound_incoherent_scattering_cross_section'], - sc.scalar(80.26, unit='barn'), - ) - assert_identical( - params['total_bound_scattering_cross_section'], sc.scalar(82.02, unit='barn') - ) - assert_identical(params['absorption_cross_section'], sc.scalar(0.3326, unit='barn')) + assert params == expected def test_scattering_params_157gd(): - params = scn.atoms.scattering_params('157Gd') - assert len(params) == 8 - assert_identical( - params['bound_coherent_scattering_length_re'], sc.scalar(-1.14, unit='fm') - ) - assert_identical( - params['bound_coherent_scattering_length_im'], sc.scalar(-71.9, unit='fm') - ) - assert_identical( - params['bound_incoherent_scattering_length_re'], - sc.scalar(5.0, variance=5.0**2, unit='fm'), - ) - assert_identical( - params['bound_incoherent_scattering_length_im'], sc.scalar(-55.8, unit='fm') - ) - assert_identical( - params['bound_coherent_scattering_cross_section'], - sc.scalar(650.0, variance=4.0**2, unit='barn'), - ) - assert_identical( - params['bound_incoherent_scattering_cross_section'], - sc.scalar(394.0, variance=7.0**2, unit='barn'), - ) - assert_identical( - params['total_bound_scattering_cross_section'], - sc.scalar(1044.0, variance=8.0**2, unit='barn'), - ) - assert_identical( - params['absorption_cross_section'], - sc.scalar(259000.0, variance=700.0**2, unit='barn'), - ) + params = scn.atoms.ScatteringParams.for_isotope('157Gd') + expected = scn.atoms.ScatteringParams( + isotope='157Gd', + coherent_scattering_length_re=sc.scalar(-1.14, unit='fm'), + coherent_scattering_length_im=sc.scalar(-71.9, unit='fm'), + incoherent_scattering_length_re=sc.scalar(5.0, variance=5.0**2, unit='fm'), + incoherent_scattering_length_im=sc.scalar(-55.8, unit='fm'), + coherent_scattering_cross_section=sc.scalar( + 650.0, variance=4.0**2, unit='barn' + ), + incoherent_scattering_cross_section=sc.scalar( + 394.0, variance=7.0**2, unit='barn' + ), + total_scattering_cross_section=sc.scalar( + 1044.0, variance=8.0**2, unit='barn' + ), + absorption_cross_section=sc.scalar(259000.0, variance=700.0**2, unit='barn'), + ) + assert params == expected def test_scattering_params_unknown(): with pytest.raises(ValueError): - scn.atoms.scattering_params('scippium') + scn.atoms.ScatteringParams.for_isotope('scippium')