From 3d4b1caabd569dca72d9e496e9241a94dc516cc2 Mon Sep 17 00:00:00 2001 From: Lukasz Mentel Date: Sat, 28 Dec 2024 17:10:51 +0100 Subject: [PATCH] [FIX] Correct melting and boiling points for Carbon allotropes (#214) * correct db values and update PhaseTransition model * reduce number of rosw in test after removing diamond * update docs * add missing boiling point for red P * update melting_point and boiling_point to return either float or None * add tests for boiling and melting_points * update type annotations * simplify tests --- ...b3cbd41_phase_transisions_extra_columns.py | 27 ++++++++ docs/source/data.rst | 64 +++++++++-------- mendeleev/elements.db | Bin 3004416 -> 3009536 bytes mendeleev/models.py | 65 ++++++++++++++++-- tests/test_element.py | 23 +++++-- tests/test_fetch.py | 2 +- 6 files changed, 138 insertions(+), 43 deletions(-) create mode 100644 alembic/versions/88ac4b3cbd41_phase_transisions_extra_columns.py diff --git a/alembic/versions/88ac4b3cbd41_phase_transisions_extra_columns.py b/alembic/versions/88ac4b3cbd41_phase_transisions_extra_columns.py new file mode 100644 index 00000000..bafc39ae --- /dev/null +++ b/alembic/versions/88ac4b3cbd41_phase_transisions_extra_columns.py @@ -0,0 +1,27 @@ +"""phase transisions extra columns + +Revision ID: 88ac4b3cbd41 +Revises: edad0a76e11e +Create Date: 2024-12-22 15:42:17.215685 + +""" + +# revision identifiers, used by Alembic. +revision = '88ac4b3cbd41' +down_revision = 'edad0a76e11e' +branch_labels = None +depends_on = None + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.add_column("phasetransitions", sa.Column("is_sublimation_point", sa.Boolean)) + op.add_column("phasetransitions", sa.Column("is_transition", sa.Boolean)) + + +def downgrade(): + with op.batch_alter_table("phasetransitions") as batch_op: + batch_op.drop_column("is_sublimation_point") + batch_op.drop_column("is_transition") diff --git a/docs/source/data.rst b/docs/source/data.rst index f68a3ab4..0218cbf9 100644 --- a/docs/source/data.rst +++ b/docs/source/data.rst @@ -147,7 +147,7 @@ The following data are currently available: +-----------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------+--------------+------------------------------------------------------+ | ``mass_number`` | Mass number of the most abundant isotope | | computed | | +-----------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------+--------------+------------------------------------------------------+ -| ``melting_point`` | Melting point | K | stored | :cite:`haynes2016crc` | +| ``melting_point`` | Melting point at 101.325 kPa pressure | K | stored | :cite:`haynes2016crc` | +-----------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------+--------------+------------------------------------------------------+ | ``mendeleev_number`` | Mendeleev's number ([#f_mendeleev_number]_) | | stored | :cite:`Pettifor1984,Villars2004` | +-----------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------+--------------+------------------------------------------------------+ @@ -292,7 +292,7 @@ Class: :py:class:`Isotope ` Isotope Decay Modes =================== -Class: :py:class:`Isotope ` +Class: :py:class:`IsotopeDecayMode ` +-----------------------------------+---------------------------------------------------------------------------------+------+--------------+--------------------+ | Attribute name | Description | Unit | Value origin | Citation keys | @@ -372,7 +372,7 @@ for compatibility. The table below provides explanations of the symbols. Atomic Scattering Factors ========================= -Class: :py:class:`Element ` +Class: :py:class:`ScatteringFactor ` +-------------------+----------------------------------------------+------+--------------+-------------------------------------------------+ | Attribute name | Description | Unit | Value origin | Citation keys | @@ -389,7 +389,7 @@ Class: :py:class:`Element ` Ionization Energies =================== -Class: :py:class:`Element ` +Class: :py:class:`IonizationEnergy ` +---------------------------+-------------------------------------------------------------------------+------+--------------+---------------+ | Attribute name | Description | Unit | Value origin | Citation keys | @@ -424,7 +424,7 @@ Class: :py:class:`Element ` Ionic Radii =========== -Class: :py:class:`Element ` +Class: :py:class:`IonicRadius ` +--------------------+-----------------------------------------+------+--------------+----------------------------------+ | Attribute name | Description | Unit | Value origin | Citation keys | @@ -460,7 +460,7 @@ by adding 14 pm to the ``ionic_radius`` values according to :cite:`Shannon1976`. Oxidation States ================ -Class: :py:class:`Element ` +Class: :py:class:`OxidationState ` +---------------------+--------------------------------------------------------------------------+------+--------------+---------------------------+ | Attribute name | Description | Unit | Value origin | Citation keys | @@ -475,32 +475,36 @@ Class: :py:class:`Element ` Phase Transitions ================= -Class: :py:class:`Element ` - -+------------------------------+--------------------------------------+------+--------------+-----------------------+ -| Attribute name | Description | Unit | Value origin | Citation keys | -+==============================+======================================+======+==============+=======================+ -| ``allotrope`` | Allotrope name | | stored | :cite:`haynes2016crc` | -+------------------------------+--------------------------------------+------+--------------+-----------------------+ -| ``atomic_number`` | Atomic number | | stored | | -+------------------------------+--------------------------------------+------+--------------+-----------------------+ -| ``boiling_point`` | Boiling point | K | stored | :cite:`haynes2016crc` | -+------------------------------+--------------------------------------+------+--------------+-----------------------+ -| ``critical_pressure`` | Critical pressure | MPa | stored | :cite:`haynes2016crc` | -+------------------------------+--------------------------------------+------+--------------+-----------------------+ -| ``critical_temperature`` | Critical temperature | K | stored | :cite:`haynes2016crc` | -+------------------------------+--------------------------------------+------+--------------+-----------------------+ -| ``melting_point`` | Melting point | K | stored | :cite:`haynes2016crc` | -+------------------------------+--------------------------------------+------+--------------+-----------------------+ -| ``triple_point_pressure`` | Pressure in kPa of the triple point | kPa | stored | :cite:`haynes2016crc` | -+------------------------------+--------------------------------------+------+--------------+-----------------------+ -| ``triple_point_temperature`` | Temperature in K of the triple point | K | stored | :cite:`haynes2016crc` | -+------------------------------+--------------------------------------+------+--------------+-----------------------+ +Class: :py:class:`PhaseTransition ` + ++------------------------------+----------------------------------------------------------------------------------------------------------------------------------+------+--------------+-----------------------+ +| Attribute name | Description | Unit | Value origin | Citation keys | ++==============================+==================================================================================================================================+======+==============+=======================+ +| ``allotrope`` | Allotrope name | | stored | :cite:`haynes2016crc` | ++------------------------------+----------------------------------------------------------------------------------------------------------------------------------+------+--------------+-----------------------+ +| ``atomic_number`` | Atomic number | | stored | | ++------------------------------+----------------------------------------------------------------------------------------------------------------------------------+------+--------------+-----------------------+ +| ``boiling_point`` | Boiling point | K | stored | :cite:`haynes2016crc` | ++------------------------------+----------------------------------------------------------------------------------------------------------------------------------+------+--------------+-----------------------+ +| ``critical_pressure`` | Critical pressure | MPa | stored | :cite:`haynes2016crc` | ++------------------------------+----------------------------------------------------------------------------------------------------------------------------------+------+--------------+-----------------------+ +| ``critical_temperature`` | Critical temperature | K | stored | :cite:`haynes2016crc` | ++------------------------------+----------------------------------------------------------------------------------------------------------------------------------+------+--------------+-----------------------+ +| ``is_sublimation_point`` | Indicates that boiling_point marks a sublimation point, where the vapor pressure of the solid phase reaches 101.325 kPa | | stored | :cite:`haynes2016crc` | ++------------------------------+----------------------------------------------------------------------------------------------------------------------------------+------+--------------+-----------------------+ +| ``is_transition`` | Indicates that melting_point marks the temperature of the transition to the crystalline form immediately below that entry | | stored | :cite:`haynes2016crc` | ++------------------------------+----------------------------------------------------------------------------------------------------------------------------------+------+--------------+-----------------------+ +| ``melting_point`` | Melting point at 101.325 kPa pressure ([#f_melting_point]_) | K | stored | :cite:`haynes2016crc` | ++------------------------------+----------------------------------------------------------------------------------------------------------------------------------+------+--------------+-----------------------+ +| ``triple_point_pressure`` | Pressure in kPa of the triple point | kPa | stored | :cite:`haynes2016crc` | ++------------------------------+----------------------------------------------------------------------------------------------------------------------------------+------+--------------+-----------------------+ +| ``triple_point_temperature`` | Temperature in K of the triple point | K | stored | :cite:`haynes2016crc` | ++------------------------------+----------------------------------------------------------------------------------------------------------------------------------+------+--------------+-----------------------+ Screening Constants =================== -Class: :py:class:`Element ` +Class: :py:class:`ScreeningConstant ` +-------------------+--------------------------------+------+--------------+-----------------------------------+ | Attribute name | Description | Unit | Value origin | Citation keys | @@ -565,6 +569,10 @@ Class: :py:class:`Element ` - Tennessine - Oganesson +.. [#f_melting_point] **melting_point** + + Melting points for carbon from the original source are not included since they are not at standard pressure. + .. [#f_electron_affinity] **electron_affinity** Electron affinities were taken from :cite:`haynes2014crc` for the elements for which the data was available. For He, Be, N, Ar and Xe affinities were taken from :cite:`Andersen2004` where they were specified for metastable ions and therefore the values are negative. diff --git a/mendeleev/elements.db b/mendeleev/elements.db index 9985e10d37d18e031e5cd31565ba5b1702cdd4fc..2cf243a49ebc33286dde6dda12913e34623ef1f1 100644 GIT binary patch delta 1964 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 Xq*3q@jKU@GiEm~z@<8~$Fy`@JX*ogz diff --git a/mendeleev/models.py b/mendeleev/models.py index d46218b9..1fa6ebdc 100644 --- a/mendeleev/models.py +++ b/mendeleev/models.py @@ -2,11 +2,13 @@ """Module defining the database models for elements and related properties.""" +from __future__ import annotations from typing import Any, Callable, Dict, List, Tuple, Union from operator import attrgetter import enum import math import urllib.parse +import warnings import numpy as np from sqlalchemy import Column, Boolean, Integer, String, Float, ForeignKey, Text, Enum @@ -290,20 +292,50 @@ def inchi(self) -> str: return f"InchI=1S/{self.symbol}" @property - def boiling_point(self) -> Union[float, Dict[str, float]]: - """Boiling point""" + def boiling_point(self) -> float | None: + """Proxy for boiling point from the ``PhaseTransition`` object. + + For elements with a single allotrope return the boiling point, + for elements with multiple allotropes where the boiling points + are equal return the boiling point, otherwise return None. + """ if len(self.phase_transitions) == 1: return self.phase_transitions[0].boiling_point + elif len(self.phase_transitions) == 2: + bps = [pt.boiling_point for pt in self.phase_transitions] + if math.isclose(*bps, rel_tol=1e-2): + return self.phase_transitions[0].boiling_point + else: + warnings.warn( + f"{self.symbol} has multiple allotropes, " + "check <{self.symbol}.phase_transitions> for details.", + UserWarning, + ) else: - return {pt.allotrope: pt.boiling_point for pt in self.phase_transitions} + return None @property - def melting_point(self) -> Union[float, Dict[str, float]]: - """Melting point""" + def melting_point(self) -> float | None: + """Proxy for melting point from the ``PhaseTransition`` object. + + For elements with a single allotrope return the melting point, + for elements with multiple allotropes where the melting points + are equal return the melting point, otherwise return None. + """ if len(self.phase_transitions) == 1: return self.phase_transitions[0].melting_point + elif len(self.phase_transitions) == 2: + mps = [pt.melting_point for pt in self.phase_transitions] + if math.isclose(*mps, rel_tol=1e-2): + return self.phase_transitions[0].melting_point + else: + warnings.warn( + f"{self.symbol} has multiple allotropes, " + "check <{self.symbol}.phase_transitions> for details.", + UserWarning, + ) else: - return {pt.allotrope: pt.melting_point for pt in self.phase_transitions} + return None @property def nist_webbook_url(self) -> str: @@ -1241,9 +1273,28 @@ class PhaseTransition(Base): triple_point_temperature = Column(Float) triple_point_pressure = Column(Float) allotrope = Column(String) + is_sublimation_point = Column(Boolean) + is_transition = Column(Boolean) def __str__(self) -> str: - return f"{self.atomic_number} Tm={self.melting_point} Tb={self.boiling_point}" + return ( + "PhaseTransition(" + + ", ".join( + [ + f"atomic_number={self.atomic_number}", + f"allotrope={self.allotrope}", + f"melting_point={self.melting_point}", + f"boiling_point={self.boiling_point}", + f"triple_point_temperature={self.triple_point_temperature}", + f"triple_point_pressure={self.triple_point_pressure}", + f"critical_temperature={self.critical_temperature}", + f"critical_pressure={self.critical_pressure}", + f"is_sublimation_point={self.is_sublimation_point}", + f"is_transition={self.is_transition}", + ] + ) + + ")" + ) def __repr__(self) -> str: return str(self) diff --git a/tests/test_element.py b/tests/test_element.py index 3502ef6d..d56ef99b 100644 --- a/tests/test_element.py +++ b/tests/test_element.py @@ -6,6 +6,7 @@ SYMBOLS = get_attribute_for_all_elements("symbol") NAMES = get_attribute_for_all_elements("name") +ELEMENTS = get_all_elements() @pytest.fixture @@ -72,14 +73,22 @@ def test_electrophilicity(symbol): e.electrophilicity() -def test__eq__(): - elements = get_all_elements() - for e in elements: - clone = element(e.symbol) - assert clone == e +@pytest.mark.parametrize("element_obj", ELEMENTS) +def test__eq__(element_obj): + clone = element(element_obj.symbol) + assert clone == element_obj def test__ne__(): - elements = get_all_elements() - for e1, e2 in zip(elements[:-1], elements[1:]): + for e1, e2 in zip(ELEMENTS[:-1], ELEMENTS[1:]): assert e1 != e2 + + +@pytest.mark.parametrize("e", ELEMENTS) +def test_melting_points_float_or_none(e): + assert isinstance(e.melting_point, (float, type(None))) + + +@pytest.mark.parametrize("e", ELEMENTS) +def test_boiling_points_float_or_none(e): + assert isinstance(e.boiling_point, (float, type(None))) diff --git a/tests/test_fetch.py b/tests/test_fetch.py index d032fb3f..b6a3e858 100644 --- a/tests/test_fetch.py +++ b/tests/test_fetch.py @@ -17,7 +17,7 @@ ("groups", 18), ("series", 10), ("oxidationstates", 601), - ("phasetransitions", 108), + ("phasetransitions", 107), ]