From f4c3bff6dea9f58722de8e9b19a24a8207b01278 Mon Sep 17 00:00:00 2001 From: dvp Date: Fri, 1 Nov 2024 15:30:48 +0300 Subject: [PATCH] notebook: mix BPE example --- .pre-commit-config.yaml | 2 +- adhoc/mix_steel_water.py | 2 +- notebooks/dvp/mix-bpe5.ipynb | 381 +++++++++++++++++++++++++++++++++++ notebooks/dvp/mix-bpe5.md | 101 ++++++++++ notebooks/dvp/mix-bpe5.py | 88 ++++++++ src/mckit/material.py | 86 ++++---- 6 files changed, 609 insertions(+), 51 deletions(-) create mode 100644 notebooks/dvp/mix-bpe5.ipynb create mode 100644 notebooks/dvp/mix-bpe5.md create mode 100644 notebooks/dvp/mix-bpe5.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 43fe7869..33838aa5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -97,7 +97,7 @@ repos: # exclude: ^notebooks|^extern - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.0 + rev: v0.7.1 hooks: - id: ruff exclude: ^notebooks|^extern diff --git a/adhoc/mix_steel_water.py b/adhoc/mix_steel_water.py index 47254758..8dfd9baa 100644 --- a/adhoc/mix_steel_water.py +++ b/adhoc/mix_steel_water.py @@ -99,7 +99,7 @@ def main() -> None: d, m = mixture_by_volume( (water_composition, water_density, water_volume_fraction), (steel_composition, steel_density, steel_volume_fraction), - number=1001, + _number=1001, ) with open("steel-water.txt", "w") as fid: print("c ==============================", file=fid) diff --git a/notebooks/dvp/mix-bpe5.ipynb b/notebooks/dvp/mix-bpe5.ipynb new file mode 100644 index 00000000..50e335cc --- /dev/null +++ b/notebooks/dvp/mix-bpe5.ipynb @@ -0,0 +1,381 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "2199ef4c", + "metadata": {}, + "source": [ + "# Compute composition of boron and polyethilen\n", + "\n", + "dvp 2024.10.31\n", + "\n", + " \n", + "Create mix of wgt5% boron and polyethilen (remaining).\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "9acb66ae-b96d-422e-a4f9-9bd7e119dc00", + "metadata": {}, + "source": [ + "## Setup" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "93a9ee6a", + "metadata": { + "execution": { + "iopub.execute_input": "2024-11-01T11:17:44.439473Z", + "iopub.status.busy": "2024-11-01T11:17:44.439189Z", + "iopub.status.idle": "2024-11-01T11:17:44.442035Z", + "shell.execute_reply": "2024-11-01T11:17:44.441645Z", + "shell.execute_reply.started": "2024-11-01T11:17:44.439453Z" + } + }, + "outputs": [], + "source": [ + "import sys\n", + "\n", + "from pathlib import Path\n", + "\n", + "from mckit import Composition, Element, Material" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "d2c89220", + "metadata": { + "execution": { + "iopub.execute_input": "2024-11-01T11:11:36.491719Z", + "iopub.status.busy": "2024-11-01T11:11:36.491472Z", + "iopub.status.idle": "2024-11-01T11:11:36.494581Z", + "shell.execute_reply": "2024-11-01T11:11:36.494204Z", + "shell.execute_reply.started": "2024-11-01T11:11:36.491700Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3.13.0 (main, Oct 22 2024, 11:20:55) [GCC 11.4.0]\n", + "/home/dvp/.pyenv/versions/3.13.0/envs/mckit\n" + ] + } + ], + "source": [ + "print(sys.version)\n", + "print(sys.prefix)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "b6e799cf", + "metadata": { + "execution": { + "iopub.execute_input": "2024-11-01T11:11:39.169715Z", + "iopub.status.busy": "2024-11-01T11:11:39.169421Z", + "iopub.status.idle": "2024-11-01T11:11:39.175452Z", + "shell.execute_reply": "2024-11-01T11:11:39.175011Z", + "shell.execute_reply.started": "2024-11-01T11:11:39.169687Z" + } + }, + "outputs": [], + "source": [ + "%config Completer.use_jedi = False" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "id": "86c55b96-6b24-4403-8f11-34f708d098b3", + "metadata": { + "execution": { + "iopub.execute_input": "2024-11-01T12:19:18.171350Z", + "iopub.status.busy": "2024-11-01T12:19:18.171152Z", + "iopub.status.idle": "2024-11-01T12:19:18.173916Z", + "shell.execute_reply": "2024-11-01T12:19:18.173544Z", + "shell.execute_reply.started": "2024-11-01T12:19:18.171333Z" + } + }, + "outputs": [], + "source": [ + "HERE = Path.cwd()\n", + "ROOT = HERE.parent.parent\n", + "dst = ROOT / \"wrk/bpe5.txt\"" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "48d719c3-82f0-4ce5-b6f1-3215b94aefc5", + "metadata": { + "execution": { + "iopub.execute_input": "2024-11-01T11:16:01.497728Z", + "iopub.status.busy": "2024-11-01T11:16:01.497525Z", + "iopub.status.idle": "2024-11-01T11:16:01.500488Z", + "shell.execute_reply": "2024-11-01T11:16:01.500095Z", + "shell.execute_reply.started": "2024-11-01T11:16:01.497711Z" + } + }, + "outputs": [], + "source": [ + "dst.parent.mkdir(exist_ok = True)" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "id": "2fca03f6-5032-4180-a840-3ff62b659fc8", + "metadata": { + "execution": { + "iopub.execute_input": "2024-11-01T12:12:08.170758Z", + "iopub.status.busy": "2024-11-01T12:12:08.170466Z", + "iopub.status.idle": "2024-11-01T12:12:08.173334Z", + "shell.execute_reply": "2024-11-01T12:12:08.172896Z", + "shell.execute_reply.started": "2024-11-01T12:12:08.170730Z" + } + }, + "outputs": [], + "source": [ + "boron_fraction = 0.05\n", + "polyethylene_fraction = 1.0 - boron_fraction\n", + "mix_number = 170023 # free slot in up-mi-24-08-27.xlsx material index for mapstp" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "0708361c-188c-4a6d-b7de-b95a3ba6d540", + "metadata": { + "execution": { + "iopub.execute_input": "2024-11-01T11:43:37.455830Z", + "iopub.status.busy": "2024-11-01T11:43:37.455625Z", + "iopub.status.idle": "2024-11-01T11:43:37.458437Z", + "shell.execute_reply": "2024-11-01T11:43:37.457995Z", + "shell.execute_reply.started": "2024-11-01T11:43:37.455813Z" + } + }, + "outputs": [], + "source": [ + "def mk_element(name: str):\n", + " return Element(name, lib=\"31c\")" + ] + }, + { + "cell_type": "markdown", + "id": "3024af99-f644-4c28-a14b-f7dce073a921", + "metadata": {}, + "source": [ + "## Computation" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "c699af3f-0349-479d-9af8-b65048d25991", + "metadata": { + "execution": { + "iopub.execute_input": "2024-11-01T11:47:14.984400Z", + "iopub.status.busy": "2024-11-01T11:47:14.983931Z", + "iopub.status.idle": "2024-11-01T11:47:14.986922Z", + "shell.execute_reply": "2024-11-01T11:47:14.986495Z", + "shell.execute_reply.started": "2024-11-01T11:47:14.984381Z" + } + }, + "outputs": [], + "source": [ + "boron = Composition(atomic=[(mk_element(\"B\"), 1.0)]).expand()" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "91e62c66-7423-47cb-9029-50c45e112f5a", + "metadata": { + "execution": { + "iopub.execute_input": "2024-11-01T11:47:15.388349Z", + "iopub.status.busy": "2024-11-01T11:47:15.388159Z", + "iopub.status.idle": "2024-11-01T11:47:15.391899Z", + "shell.execute_reply": "2024-11-01T11:47:15.391386Z", + "shell.execute_reply.started": "2024-11-01T11:47:15.388333Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'MNone 5010.31c 1.990000e-01\\n 5011.31c 8.010000e-01'" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "boron.mcnp_repr()" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "id": "bd56919e-d2fe-4aa4-aaa0-6b2d1db50f11", + "metadata": { + "execution": { + "iopub.execute_input": "2024-11-01T12:09:38.537013Z", + "iopub.status.busy": "2024-11-01T12:09:38.536821Z", + "iopub.status.idle": "2024-11-01T12:09:38.539707Z", + "shell.execute_reply": "2024-11-01T12:09:38.539334Z", + "shell.execute_reply.started": "2024-11-01T12:09:38.536997Z" + } + }, + "outputs": [], + "source": [ + "polyethylene = Composition(atomic = [(mk_element(\"C\"), 1.0), (mk_element(\"H\"), 2.0)]).expand()" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "id": "b33a1a5c-0d47-40a3-baef-f05f340419de", + "metadata": { + "execution": { + "iopub.execute_input": "2024-11-01T12:09:38.921739Z", + "iopub.status.busy": "2024-11-01T12:09:38.921544Z", + "iopub.status.idle": "2024-11-01T12:09:38.925044Z", + "shell.execute_reply": "2024-11-01T12:09:38.924632Z", + "shell.execute_reply.started": "2024-11-01T12:09:38.921723Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'MNone 6012.31c 3.297667e-01\\n 6013.31c 3.566667e-03\\n 1001.31c 6.665900e-01\\n 1002.31c 7.666667e-05'" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "polyethylene.mcnp_repr()" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "id": "6fc43fa7-59f7-4c09-9f6f-c7db03e7da66", + "metadata": { + "execution": { + "iopub.execute_input": "2024-11-01T12:12:23.534305Z", + "iopub.status.busy": "2024-11-01T12:12:23.534109Z", + "iopub.status.idle": "2024-11-01T12:12:23.537104Z", + "shell.execute_reply": "2024-11-01T12:12:23.536651Z", + "shell.execute_reply.started": "2024-11-01T12:12:23.534288Z" + } + }, + "outputs": [], + "source": [ + "bpe5 = Composition.mixture(\n", + " (boron, boron_fraction / boron.molar_mass),\n", + " (polyethylene, polyethylene_fraction / polyethilen.molar_mass),\n", + ").rename(mix_number)" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "3bd9fd5a-d3d3-4d06-b50f-29eb3f4803a3", + "metadata": { + "execution": { + "iopub.execute_input": "2024-11-01T12:12:24.485228Z", + "iopub.status.busy": "2024-11-01T12:12:24.485037Z", + "iopub.status.idle": "2024-11-01T12:12:24.488807Z", + "shell.execute_reply": "2024-11-01T12:12:24.488238Z", + "shell.execute_reply.started": "2024-11-01T12:12:24.485212Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'M170023 5010.31c 4.428836e-03\\n 5011.31c 1.782662e-02\\n 6012.31c 3.224276e-01\\n 6013.31c 3.487289e-03\\n 1001.31c 6.517547e-01\\n 1002.31c 7.496041e-05'" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bpe5.mcnp_repr()" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "id": "38c768c5-1e9e-4444-aad7-582fbf13a207", + "metadata": { + "execution": { + "iopub.execute_input": "2024-11-01T12:19:49.036802Z", + "iopub.status.busy": "2024-11-01T12:19:49.036603Z", + "iopub.status.idle": "2024-11-01T12:19:49.040748Z", + "shell.execute_reply": "2024-11-01T12:19:49.040327Z", + "shell.execute_reply.started": "2024-11-01T12:19:49.036786Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "192" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dst.write_text(bpe5.mcnp_repr().replace(\"170023\", \"17023\\n \"))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "904e1747-e9b0-44c5-854e-f87f3e610a1c", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/dvp/mix-bpe5.md b/notebooks/dvp/mix-bpe5.md new file mode 100644 index 00000000..f20955a5 --- /dev/null +++ b/notebooks/dvp/mix-bpe5.md @@ -0,0 +1,101 @@ +--- +jupyter: + jupytext: + text_representation: + extension: .md + format_name: markdown + format_version: '1.3' + jupytext_version: 1.16.4 + kernelspec: + display_name: Python 3 (ipykernel) + language: python + name: python3 +--- + + +# Compute composition of boron and polyethilen + +dvp 2024.10.31 + + +Create mix of wgt5% boron and polyethilen (remaining). + + + + +## Setup + +```python +import sys + +from pathlib import Path + +from mckit import Composition, Element, Material +``` + +```python +print(sys.version) +print(sys.prefix) +``` + +```python +%config Completer.use_jedi = False +``` + +```python +HERE = Path.cwd() +ROOT = HERE.parent.parent +dst = ROOT / "wrk/bpe5.txt" +``` + +```python +dst.parent.mkdir(exist_ok = True) +``` + +```python +boron_fraction = 0.05 +polyethylene_fraction = 1.0 - boron_fraction +mix_number = 170023 # free slot in up-mi-24-08-27.xlsx material index for mapstp +``` + +```python +def mk_element(name: str): + return Element(name, lib="31c") +``` + +## Computation + +```python +boron = Composition(atomic=[(mk_element("B"), 1.0)]).expand() +``` + +```python +boron.mcnp_repr() +``` + +```python +polyethylene = Composition(atomic = [(mk_element("C"), 1.0), (mk_element("H"), 2.0)]).expand() +``` + +```python +polyethylene.mcnp_repr() +``` + +```python +bpe5 = Composition.mixture( + (boron, boron_fraction / boron.molar_mass), + (polyethylene, polyethylene_fraction / polyethilen.molar_mass), +).rename(mix_number) +``` + +```python +bpe5.mcnp_repr() +``` + +```python +dst.write_text(bpe5.mcnp_repr().replace("170023", "17023\n ")) +``` + +```python + +``` diff --git a/notebooks/dvp/mix-bpe5.py b/notebooks/dvp/mix-bpe5.py new file mode 100644 index 00000000..d681ed3b --- /dev/null +++ b/notebooks/dvp/mix-bpe5.py @@ -0,0 +1,88 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.16.4 +# kernelspec: +# display_name: Python 3 (ipykernel) +# language: python +# name: python3 +# --- + +# %% [markdown] +# # Compute composition of boron and polyethilen +# +# dvp 2024.10.31 +# +# +# Create mix of wgt5% boron and polyethilen (remaining). +# +# + +# %% [markdown] +# ## Setup + +# %% +import sys + +from pathlib import Path + +from mckit import Composition, Element, Material + +# %% +print(sys.version) +print(sys.prefix) + +# %% +# %config Completer.use_jedi = False + +# %% +HERE = Path.cwd() +ROOT = HERE.parent.parent +dst = ROOT / "wrk/bpe5.txt" + +# %% +dst.parent.mkdir(exist_ok = True) + +# %% +boron_fraction = 0.05 +polyethylene_fraction = 1.0 - boron_fraction +mix_number = 170023 # free slot in up-mi-24-08-27.xlsx material index for mapstp + + +# %% +def mk_element(name: str): + return Element(name, lib="31c") + + +# %% [markdown] +# ## Computation + +# %% +boron = Composition(atomic=[(mk_element("B"), 1.0)]).expand() + +# %% +boron.mcnp_repr() + +# %% +polyethylene = Composition(atomic = [(mk_element("C"), 1.0), (mk_element("H"), 2.0)]).expand() + +# %% +polyethylene.mcnp_repr() + +# %% +bpe5 = Composition.mixture( + (boron, boron_fraction / boron.molar_mass), + (polyethylene, polyethylene_fraction / polyethilen.molar_mass), +).rename(mix_number) + +# %% +bpe5.mcnp_repr() + +# %% +dst.write_text(bpe5.mcnp_repr().replace("170023", "17023\n ")) + +# %% diff --git a/src/mckit/material.py b/src/mckit/material.py index 780b047a..92619c06 100644 --- a/src/mckit/material.py +++ b/src/mckit/material.py @@ -190,49 +190,46 @@ def __iter__(self): def __contains__(self, item: str | Element) -> bool: """Checks if the composition contains the item. - Parameters - ---------- - item : - Isotope. It can be either isotope name or Element instance. + Args: + item: Isotope. It can be either isotope name or Element instance. Returns: - ------- - result : True if the composition contains the isotope, False otherwise. """ if not isinstance(item, Element): item = Element(item) + return item in self._composition - def get_atomic(self, isotope: str | Element) -> float: + def get_atomic(self, _isotope: str | Element) -> float: """Gets atomic fraction of the isotope. Raises KeyError if the composition doesn't contain the isotope. Args: - isotope: Isotope. It can be either isotope name or Element instance. + _isotope: Isotope. It can be either isotope name or Element instance. Returns: Atomic fraction of the specified isotope. """ - if not isinstance(isotope, Element): - isotope = Element(isotope) - return self._composition[isotope] + if not isinstance(_isotope, Element): + _isotope = Element(_isotope) + return self._composition[_isotope] - def get_weight(self, isotope: int | Element) -> float: + def get_weight(self, _isotope: int | Element) -> float: """Gets weight fraction of the isotope. Raises KeyError if the composition doesn't contain the isotope. Args: - isotope : Isotope. It can be either isotope name or Element instance. + _isotope : Isotope. It can be either isotope name or Element instance. Returns: Weight fraction of the specified isotope. """ - if not isinstance(isotope, Element): - isotope = Element(isotope) - frac: float = self._composition[isotope] * isotope.molar_mass / self._molar_mass + if not isinstance(_isotope, Element): + _isotope = Element(_isotope) + frac: float = self._composition[_isotope] * _isotope.molar_mass / self._molar_mass return frac @property @@ -248,33 +245,28 @@ def expand(self) -> Composition: """ composition: dict[Element, float] = {} already = True - for el, conc in self._composition.items(): + for el, concentration in self._composition.items(): if el.mass_number == 0: already = False - for isotope, frac in el.expand().items(): - if isotope not in composition.keys(): - composition[isotope] = 0 - composition[isotope] += conc * frac + for _isotope, frac in el.expand().items(): + if _isotope not in composition.keys(): + composition[_isotope] = 0 + composition[_isotope] += concentration * frac if already: return self return Composition(atomic=composition.items(), **self.options) - def natural(self, tolerance=1.0e-8) -> Composition | None: + def natural(self, tolerance: float = 1.0e-8) -> Composition | None: """Tries to replace detailed isotope composition by natural elements. Modifies current object. - Parameters - ---------- - tolerance : float - Relative tolerance to consider isotope fractions as equal. - Default: 1.e-8 + Args: + tolerance: Relative tolerance to consider isotope fractions as equal. Default: 1.e-8 Returns: - ------- - comp : Composition - self, if composition is reduced successfully to natural. None returned if the - composition cannot be reduced to natural. + self - if composition is reduced successfully to natural. + None - if the composition cannot be reduced to natural. """ already = True by_charge: dict[int, dict[int, float]] = {} @@ -319,14 +311,10 @@ def elements(self) -> Iterable[Element]: def mixture(*compositions: tuple[Composition, float]) -> Composition: """Makes mixture of the compositions with specific fractions. - Parameters - ---------- - compositions : - List of pairs composition, fraction. + Args: + compositions: List of pairs composition, fraction. Returns: - ------- - mix : Mixture. """ atomics = [] @@ -340,13 +328,13 @@ def mixture(*compositions: tuple[Composition, float]) -> Composition: def mixture_by_volume( *fractions_spec: tuple[Composition, float, float], - number=0, + _number=0, ) -> tuple[float, Composition]: """Compute mix of compositions defined with densities and volume fractions. Args: fractions_spec: list of specs (Composition, density, volume_fraction) - number: ... to assign as composition 'name' + _number: ... to assign as composition 'name' Returns: Composition: the mix by atomic fractions @@ -362,7 +350,7 @@ def mixture_by_volume( total_moles = moles.sum() atomic_fractions = moles / total_moles mix = Composition.mixture(*zip(compositions, atomic_fractions, strict=False)) - mix.options["name"] = number + mix.options["name"] = _number density = sum(density * volume_fraction for _, density, volume_fraction in fractions_spec) return density, mix @@ -624,19 +612,19 @@ def __init__(self, _name: str | int, lib=None, isomer=0, comment=None): self._isomer = isomer self._comment = comment - def __hash__(self): + def __hash__(self) -> int: return self._charge * (self._mass_number + 1) * (self._isomer + 1) - def __eq__(self, other): - if ( + def __eq__(self, other) -> bool: + if not isinstance(other, Element): + return False + return ( self._charge == other.charge and self._mass_number == other.mass_number and self._isomer == other._isomer - ): - return True - return False + ) - def __str__(self): + def __str__(self) -> str: _name = _CHARGE_TO_NAME[self.charge].capitalize() if self._mass_number > 0: _name += "-" + str(self._mass_number) @@ -646,14 +634,14 @@ def __str__(self): _name += str(self._isomer - 1) return _name - def mcnp_repr(self): + def mcnp_repr(self) -> str: """Gets MCNP representation of the element.""" _name = str(self.charge * 1000 + self.mass_number) if self.lib is not None: _name += f".{self.lib}" return _name - def fispact_repr(self): + def fispact_repr(self) -> str: """Gets FISPACT representation of the element.""" _name = _CHARGE_TO_NAME[self.charge].capitalize() if self._mass_number > 0: