From 538efbb58ddf613c4b1c696b255bb2adbc1e516e Mon Sep 17 00:00:00 2001 From: Kyle Oman Date: Thu, 19 Sep 2024 10:16:20 +0100 Subject: [PATCH 01/14] Fix an issue creating a circular import externally and tidy up unusued imports. --- swiftsimio/masks.py | 2 +- swiftsimio/reader.py | 21 ++++++++------------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/swiftsimio/masks.py b/swiftsimio/masks.py index b3f406ca..f5db74a8 100644 --- a/swiftsimio/masks.py +++ b/swiftsimio/masks.py @@ -10,7 +10,7 @@ import numpy as np -from swiftsimio import SWIFTMetadata +from swiftsimio.metadata.objects import SWIFTMetadata from swiftsimio.objects import InvalidSnapshot diff --git a/swiftsimio/reader.py b/swiftsimio/reader.py index 809b3817..f2e6ec61 100644 --- a/swiftsimio/reader.py +++ b/swiftsimio/reader.py @@ -11,27 +11,20 @@ + SWIFTDataset, a container class for all of the above. """ - from swiftsimio.accelerated import read_ranges_from_file -from swiftsimio.objects import cosmo_array, cosmo_factor, a +from swiftsimio.objects import cosmo_array, cosmo_factor from swiftsimio.metadata.objects import ( metadata_discriminator, SWIFTUnits, SWIFTGroupMetadata, - SWIFTMetadata, ) -import re import h5py import unyt import numpy as np -import warnings - -from datetime import datetime -from pathlib import Path -from typing import Union, Callable, List, Optional +from typing import Union, List def generate_getter( @@ -181,9 +174,11 @@ def getter(self): cosmo_array( # Only use column data if array is multidimensional, otherwise # we will crash here - handle[field][:, columns] - if handle[field].ndim > 1 - else handle[field][:], + ( + handle[field][:, columns] + if handle[field].ndim > 1 + else handle[field][:] + ), unit, cosmo_factor=cosmo_factor, name=description, @@ -316,7 +311,7 @@ def generate_empty_properties(self): class __SWIFTNamedColumnDataset(object): """ Holder class for individual named datasets. Very similar to - __SWIFTGroupsDatasets but much simpler. + __SWIFTGroupDatasets but much simpler. """ def __init__(self, field_path: str, named_columns: List[str], name: str): From f7c8d754b6cf92a2b228ee5f866b3cfdaede7959 Mon Sep 17 00:00:00 2001 From: Kyle Oman Date: Thu, 19 Sep 2024 10:35:02 +0100 Subject: [PATCH 02/14] Remove some more unused imports. --- swiftsimio/subset_writer.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/swiftsimio/subset_writer.py b/swiftsimio/subset_writer.py index 235cbbe2..da7b03f3 100644 --- a/swiftsimio/subset_writer.py +++ b/swiftsimio/subset_writer.py @@ -3,12 +3,10 @@ it to a new file. """ -from swiftsimio.reader import SWIFTUnits, SWIFTMetadata from swiftsimio.masks import SWIFTMask from swiftsimio.accelerated import read_ranges_from_file import swiftsimio.metadata as metadata -import unyt import h5py import numpy as np from typing import Optional, List From 34994bb694c7924ce0a0500b8f9befa4b1ee4abf Mon Sep 17 00:00:00 2001 From: Kyle Oman Date: Thu, 19 Sep 2024 10:35:14 +0100 Subject: [PATCH 03/14] Remove guard for SOAP files only. --- swiftsimio/masks.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/swiftsimio/masks.py b/swiftsimio/masks.py index f5db74a8..9564f5ae 100644 --- a/swiftsimio/masks.py +++ b/swiftsimio/masks.py @@ -423,9 +423,6 @@ def constrain_index(self, index: int): index : int The index of the row to select. """ - if not self.metadata.filetype == "SOAP": - warnings.warn("Not masking a SOAP catalogue, nothing constrained.") - return for group_name in self.metadata.present_group_names: setattr(self, group_name, np.array([[index, index + 1]])) setattr(self, f"{group_name}_size", 1) From 19e069f58bc693b99fd3e50c818cd85af277bff8 Mon Sep 17 00:00:00 2001 From: Josh Borrow Date: Thu, 19 Sep 2024 17:25:43 +0200 Subject: [PATCH 04/14] Removed manual pointers --- swiftsimio/masks.py | 62 ++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/swiftsimio/masks.py b/swiftsimio/masks.py index a19ba417..7b2cda63 100644 --- a/swiftsimio/masks.py +++ b/swiftsimio/masks.py @@ -23,6 +23,9 @@ class SWIFTMask(object): Main masking object. This can have masks for any present particle field in it. Pass in the SWIFTMetadata. """ + + group_mapping: dict | None = None + group_size_mapping: dict | None = None def __init__(self, metadata: SWIFTMetadata, spatial_only=True): """ @@ -73,6 +76,9 @@ def _generate_mapping_dictionary(self) -> dict[str, str]: names. Allows for pointers to be used instead of re-creating masks. """ + if self.group_mapping is not None: + return self.group_mapping + if self.metadata.shared_cell_counts is None: # Each and every particle type has its own cell counts, offsets, # and hence masks. @@ -87,6 +93,29 @@ def _generate_mapping_dictionary(self) -> dict[str, str]: return self.group_mapping + def _generate_size_mapping_dictionary(self) -> dict[str, str]: + """ + Creates cross-links between 'group names' and their underlying cell metadata + names. Allows for pointers to be used instead of re-creating masks. + """ + + if self.group_size_mapping is not None: + return self.group_size_mapping + + if self.metadata.shared_cell_counts is None: + # Each and every particle type has its own cell counts, offsets, + # and hence masks. + self.group_size_mapping = { + f"{group}_size": f"_{group}_size" for group in self.metadata.present_group_names + } + else: + # We actually only have _one_ mask! + self.group_size_mapping = { + f"{group}_size": "_shared_size" for group in self.metadata.present_group_names + } + + return self.group_size_mapping + def _generate_update_list(self) -> list[str]: """ Gets a list of internal mask variables that need to be updated when @@ -100,22 +129,21 @@ def _generate_update_list(self) -> list[str]: else: # We actually only have _one_ mask! return ["_shared"] - - def _create_pointers(self): - # Create pointers for every single particle type. - for group_name, data_name in self._generate_mapping_dictionary().items(): - setattr( - self, - group_name, - getattr(self, data_name) - ) - setattr( - self, - f"{group_name}_size", - getattr(self, f"{data_name}_size") - ) + def __getattr__(self, name): + """ + Overloads the getattr method to allow for direct access to the masks + for each particle type. + """ + mappings = {**self._generate_mapping_dictionary(), **self._generate_size_mapping_dictionary()} + + underlying_name = mappings.get(name, None) + if underlying_name is not None: + return getattr(self, underlying_name) + + raise AttributeError(f"Attribute {name} not found in SWIFTMask") + def _generate_empty_masks(self): """ @@ -137,8 +165,6 @@ def _generate_empty_masks(self): setattr(self, data_name, np.ones(size, dtype=bool)) setattr(self, f"{data_name}_size", size) - self._create_pointers() - return def _unpack_cell_metadata(self): @@ -451,8 +477,6 @@ def constrain_spatial(self, restrict, intersect: bool = False): for mask in self._generate_update_list(): self._update_spatial_mask(restrict, mask, self.cell_mask) - self._create_pointers() - return def convert_masks_to_ranges(self): @@ -477,8 +501,6 @@ def convert_masks_to_ranges(self): print(mask, where_array) setattr(self, mask, ranges_from_array(where_array)) - self._create_pointers() - return def constrain_index(self, index: int): From 62ae2b38dbfdf144195541e5e2f46e070c6710ea Mon Sep 17 00:00:00 2001 From: Josh Borrow Date: Thu, 19 Sep 2024 17:26:18 +0200 Subject: [PATCH 05/14] Formatting --- swiftsimio/masks.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/swiftsimio/masks.py b/swiftsimio/masks.py index d197250b..5ee97e21 100644 --- a/swiftsimio/masks.py +++ b/swiftsimio/masks.py @@ -23,7 +23,7 @@ class SWIFTMask(object): Main masking object. This can have masks for any present particle field in it. Pass in the SWIFTMetadata. """ - + group_mapping: dict | None = None group_size_mapping: dict | None = None @@ -91,7 +91,7 @@ def _generate_mapping_dictionary(self) -> dict[str, str]: } return self.group_mapping - + def _generate_size_mapping_dictionary(self) -> dict[str, str]: """ Creates cross-links between 'group names' and their underlying cell metadata @@ -105,16 +105,18 @@ def _generate_size_mapping_dictionary(self) -> dict[str, str]: # Each and every particle type has its own cell counts, offsets, # and hence masks. self.group_size_mapping = { - f"{group}_size": f"_{group}_size" for group in self.metadata.present_group_names + f"{group}_size": f"_{group}_size" + for group in self.metadata.present_group_names } else: # We actually only have _one_ mask! self.group_size_mapping = { - f"{group}_size": "_shared_size" for group in self.metadata.present_group_names + f"{group}_size": "_shared_size" + for group in self.metadata.present_group_names } return self.group_size_mapping - + def _generate_update_list(self) -> list[str]: """ Gets a list of internal mask variables that need to be updated when @@ -134,15 +136,17 @@ def __getattr__(self, name): Overloads the getattr method to allow for direct access to the masks for each particle type. """ - mappings = {**self._generate_mapping_dictionary(), **self._generate_size_mapping_dictionary()} + mappings = { + **self._generate_mapping_dictionary(), + **self._generate_size_mapping_dictionary(), + } underlying_name = mappings.get(name, None) if underlying_name is not None: return getattr(self, underlying_name) - + raise AttributeError(f"Attribute {name} not found in SWIFTMask") - def _generate_empty_masks(self): """ From 1b6646df9c4d6a4a26bf9dcf852d9277cab61296 Mon Sep 17 00:00:00 2001 From: Josh Borrow Date: Thu, 19 Sep 2024 17:28:48 +0200 Subject: [PATCH 06/14] Removed print statement --- swiftsimio/masks.py | 1 - 1 file changed, 1 deletion(-) diff --git a/swiftsimio/masks.py b/swiftsimio/masks.py index 5ee97e21..5b9efde5 100644 --- a/swiftsimio/masks.py +++ b/swiftsimio/masks.py @@ -502,7 +502,6 @@ def convert_masks_to_ranges(self): for mask in self._generate_update_list(): where_array = np.where(getattr(self, mask))[0] setattr(self, f"{mask}_size", where_array.size) - print(mask, where_array) setattr(self, mask, ranges_from_array(where_array)) return From 700ab972ce221d66cb228381d2b6742efeddeb15 Mon Sep 17 00:00:00 2001 From: Josh Borrow Date: Thu, 19 Sep 2024 19:25:54 +0200 Subject: [PATCH 07/14] Working --- swiftsimio/masks.py | 9 +++++---- tests/test_soap.py | 11 +++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/swiftsimio/masks.py b/swiftsimio/masks.py index 5b9efde5..2bd560e6 100644 --- a/swiftsimio/masks.py +++ b/swiftsimio/masks.py @@ -277,9 +277,10 @@ def constrain_mask( """ if self.spatial_only: - print("You cannot constrain a mask if spatial_only=True") - print("Please re-initialise the SWIFTMask object with spatial_only=False") - return + raise ValueError( + "You cannot constrain a mask if spatial_only=True. " + "Please re-initialise the SWIFTMask object with spatial_only=False" + ) mapping = self._generate_mapping_dictionary() data_name = mapping[group_name] @@ -526,7 +527,7 @@ def constrain_index(self, index: int): return def get_masked_counts_offsets( - self + self, ) -> tuple[dict[str, np.array], dict[str, np.array]]: """ Returns the particle counts and offsets in cells selected by the mask diff --git a/tests/test_soap.py b/tests/test_soap.py index b27defa6..6ae8b398 100644 --- a/tests/test_soap.py +++ b/tests/test_soap.py @@ -36,3 +36,14 @@ def test_soap_can_mask_non_spatial(filename): data = load(filename, mask=this_mask) data.spherical_overdensity_200_mean.total_mass[0] + + +@requires("soap_example.hdf5") +def test_soap_can_mask_spatial_and_non_spatial_actually_use(filename): + this_mask = mask(filename, spatial_only=False) + + this_mask.constrain_mask("spherical_overdensity_200_mean", "total_mass", 1e5, 1e9) + + data = load(filename, mask=this_mask) + + data.spherical_overdensity_200_mean.total_mass[0] \ No newline at end of file From 702e0ba6050fe3307cbeba0e16177113a42ec1a6 Mon Sep 17 00:00:00 2001 From: Kyle Oman Date: Thu, 19 Sep 2024 20:54:45 +0100 Subject: [PATCH 08/14] Update constrain_index for _shared mask. --- swiftsimio/masks.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/swiftsimio/masks.py b/swiftsimio/masks.py index 2bd560e6..028b7853 100644 --- a/swiftsimio/masks.py +++ b/swiftsimio/masks.py @@ -521,9 +521,9 @@ def constrain_index(self, index: int): if not self.metadata.filetype == "SOAP": warnings.warn("Not masking a SOAP catalogue, nothing constrained.") return - for group_name in self.metadata.present_group_names: - setattr(self, group_name, np.array([[index, index + 1]])) - setattr(self, f"{group_name}_size", 1) + self._shared = np.array([[index, index + 1]]) + self._shared_size = 1 + return def get_masked_counts_offsets( From 74b91b81961a63f3ce74add880f8eac5ef77be16 Mon Sep 17 00:00:00 2001 From: Josh Borrow Date: Fri, 20 Sep 2024 09:30:15 +0200 Subject: [PATCH 09/14] Allow constraining on a single row --- swiftsimio/masks.py | 21 ++++++++++++++++----- swiftsimio/metadata/objects.py | 8 ++++++++ tests/test_soap.py | 13 ++++++++++++- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/swiftsimio/masks.py b/swiftsimio/masks.py index 028b7853..e752ca54 100644 --- a/swiftsimio/masks.py +++ b/swiftsimio/masks.py @@ -518,11 +518,22 @@ def constrain_index(self, index: int): index : int The index of the row to select. """ - if not self.metadata.filetype == "SOAP": - warnings.warn("Not masking a SOAP catalogue, nothing constrained.") - return - self._shared = np.array([[index, index + 1]]) - self._shared_size = 1 + + if not self.metadata.homogeneous_arrays: + raise RuntimeError( + "Cannot constrain to a single row in a non-homogeneous array; you currently " + f"are using a {self.metadata.output_type} file" + ) + + if not self.spatial_only: + raise RuntimeError( + "Cannot constrain to a single row in a non-spatial mask; you currently " + "are using a non-spatial mask" + ) + + for mask in self._generate_update_list(): + setattr(self, mask, np.array([[index, index + 1]])) + setattr(self, f"{mask}_size", 1) return diff --git a/swiftsimio/metadata/objects.py b/swiftsimio/metadata/objects.py index 206a05ad..b47a7938 100644 --- a/swiftsimio/metadata/objects.py +++ b/swiftsimio/metadata/objects.py @@ -45,6 +45,11 @@ class SWIFTMetadata(ABC): # (as is the case in SOAP) or whether each type (e.g. Gas, Dark Matter, etc.) # has its own top-level cell grid counts. shared_cell_counts: str | None = None + # Whether all the arrays in this files have the same length and order (as is + # the case for SOAP, all arrays correspond to subhalos) or whether there are + # multiple types (e.g. Gas, Dark Matter, etc.). Allows you to use constrain_index + # in masking as everyone uses the same _shared mask! + homogeneous_arrays: bool = False @abstractmethod def __init__(self, filename, units: "SWIFTUnits"): @@ -1223,6 +1228,8 @@ class SWIFTFOFMetadata(SWIFTMetadata): class. """ + homogeneous_arrays: bool = True + def __init__(self, filename: str, units: SWIFTUnits): self.filename = filename self.units = units @@ -1265,6 +1272,7 @@ class SWIFTSOAPMetadata(SWIFTMetadata): masking_valid: bool = True shared_cell_counts: str = "Subhalos" + homogeneous_arrays: bool = True def __init__(self, filename: str, units: SWIFTUnits): self.filename = filename diff --git a/tests/test_soap.py b/tests/test_soap.py index 6ae8b398..2480874d 100644 --- a/tests/test_soap.py +++ b/tests/test_soap.py @@ -46,4 +46,15 @@ def test_soap_can_mask_spatial_and_non_spatial_actually_use(filename): data = load(filename, mask=this_mask) - data.spherical_overdensity_200_mean.total_mass[0] \ No newline at end of file + data.spherical_overdensity_200_mean.total_mass[0] + + +@requires("soap_example.hdf5") +def test_soap_single_row_mask(filename): + this_mask = mask(filename, spatial_only=True) + + this_mask.constrain_index(21) + + data = load(filename, mask=this_mask) + + assert len(data.spherical_overdensity_200_mean.total_mass) == 1 From 6e2e96e115d4f8e835b13a922550aaef7df37468 Mon Sep 17 00:00:00 2001 From: Josh Borrow Date: Fri, 20 Sep 2024 09:37:20 +0200 Subject: [PATCH 10/14] Added constrain_indices --- swiftsimio/masks.py | 37 +++++++++++++++++++++++++++++++++++++ tests/test_soap.py | 26 ++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/swiftsimio/masks.py b/swiftsimio/masks.py index e752ca54..51ada35d 100644 --- a/swiftsimio/masks.py +++ b/swiftsimio/masks.py @@ -537,6 +537,43 @@ def constrain_index(self, index: int): return + def constrain_indices(self, indices: list[int]): + """ + Constrain the mask to a list of rows. + Parameters + ---------- + indices : list[int] + An list of the indices of the rows to mask. + """ + + if not self.metadata.homogeneous_arrays: + raise RuntimeError( + "Cannot constrain to a single row in a non-homogeneous array; you currently " + f"are using a {self.metadata.output_type} file" + ) + + if self.spatial_only: + if len(indices) > 1000: + warnings.warn( + "You are constraining a large number of indices with a spatial " + "mask, potentially leading to lots of overlap. You should " + "use a non-spatial mask (i.e. spatial_only=False)" + ) + + for mask in self._generate_update_list(): + setattr(self, mask, np.array([[i, i + 1] for i in indices])) + setattr(self, f"{mask}_size", len(indices)) + + else: + for mask in self._generate_update_list(): + comparison_array = np.zeros(getattr(self, mask).size, dtype=bool) + comparison_array[indices] = True + setattr( + self, mask, np.logical_and(getattr(self, mask), comparison_array) + ) + + return + def get_masked_counts_offsets( self, ) -> tuple[dict[str, np.array], dict[str, np.array]]: diff --git a/tests/test_soap.py b/tests/test_soap.py index 2480874d..c30727eb 100644 --- a/tests/test_soap.py +++ b/tests/test_soap.py @@ -58,3 +58,29 @@ def test_soap_single_row_mask(filename): data = load(filename, mask=this_mask) assert len(data.spherical_overdensity_200_mean.total_mass) == 1 + + +@requires("soap_example.hdf5") +def test_soap_multiple_row_mask_non_spatial(filename): + this_mask = mask(filename, spatial_only=False) + + IDC = [0, 1, 2, 3, 6, 23, 94, 57] + + this_mask.constrain_indices(IDC) + + data = load(filename, mask=this_mask) + + assert len(data.spherical_overdensity_200_mean.total_mass) == len(IDC) + + +@requires("soap_example.hdf5") +def test_soap_multiple_row_mask_spatial(filename): + this_mask = mask(filename, spatial_only=False) + + IDC = [0, 1, 2, 3, 6, 23, 94, 57] + + this_mask.constrain_indices(IDC) + + data = load(filename, mask=this_mask) + + assert len(data.spherical_overdensity_200_mean.total_mass) == len(IDC) From e206298a4913d199c872984634be47c1e3cf8911 Mon Sep 17 00:00:00 2001 From: Josh Borrow Date: Fri, 20 Sep 2024 09:43:01 +0200 Subject: [PATCH 11/14] Added docs for row reading --- docs/source/masking/index.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/source/masking/index.rst b/docs/source/masking/index.rst index e597ff8c..a5e9d4c6 100644 --- a/docs/source/masking/index.rst +++ b/docs/source/masking/index.rst @@ -123,6 +123,31 @@ ask for the temperature of particles, it will recieve an array containing temperatures of particles that lie in the region [0.2, 0.7] and have a density between 0.4 and 0.8 g/cm^3. +Row Masking +----------- + +For certian scenarios, in particular halo catalogues, all arrays are of the +same length (you can check this through the ``metadata.homogeneous_arrays`` +attribute). Often, you are interested in a handful of, or a single, row, +corresponding to the properties of a particular object. You can use the +methods ``constrain_index`` and ``constrain_indices`` to do this, which +return ``swiftsimio`` data objects containing arrays with only those +rows. + +.. code-block:: python + + import swiftsimio as sw + + mask = sw.mask(filename) + + mask.constrain_indices([1, 99, 23421]) + + data = sw.load(filename, mask=mask) + +Here, the length of all the arrays will be 3. A quick performance note: if you +are using many indices (over 1000), you will want to set ``spatial_only=False`` +to potentially benefit from range reading of overlapping rows in a single chunk. + Writing subset of snapshot -------------------------- In some cases it may be useful to write a subset of an existing snapshot to its From 8c68c3a8ba03d099454fd2a882ea555370e309ed Mon Sep 17 00:00:00 2001 From: Josh Borrow Date: Fri, 20 Sep 2024 09:49:10 +0200 Subject: [PATCH 12/14] Make test more complex to try to understand failure --- tests/test_soap.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/tests/test_soap.py b/tests/test_soap.py index c30727eb..b584ee27 100644 --- a/tests/test_soap.py +++ b/tests/test_soap.py @@ -5,6 +5,7 @@ from tests.helper import requires from swiftsimio import load, mask +import unyt @requires("soap_example.hdf5") @@ -42,11 +43,24 @@ def test_soap_can_mask_non_spatial(filename): def test_soap_can_mask_spatial_and_non_spatial_actually_use(filename): this_mask = mask(filename, spatial_only=False) - this_mask.constrain_mask("spherical_overdensity_200_mean", "total_mass", 1e5, 1e9) + lower = unyt.unyt_quantity(1e5, "Msun") + upper = unyt.unyt_quantity(1e13, "Msun") + this_mask.constrain_mask("spherical_overdensity_200_mean", "total_mass", lower, upper) data = load(filename, mask=this_mask) - data.spherical_overdensity_200_mean.total_mass[0] + masses = data.spherical_overdensity_200_mean.total_mass + + assert len(masses) > 0 + + data2 = load(filename) + + masses2 = data2.spherical_overdensity_200_mean.total_mass + + # Manually mask + custom_mask = (masses2 >= lower) & (masses2 <= upper) + + assert len(masses2[custom_mask]) == len(masses) @requires("soap_example.hdf5") From 71b2f94be64bc02d1aeec31070b022c4cb275d76 Mon Sep 17 00:00:00 2001 From: Josh Borrow Date: Fri, 20 Sep 2024 09:50:00 +0200 Subject: [PATCH 13/14] Formatting --- tests/test_soap.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_soap.py b/tests/test_soap.py index b584ee27..ad8274ea 100644 --- a/tests/test_soap.py +++ b/tests/test_soap.py @@ -45,7 +45,9 @@ def test_soap_can_mask_spatial_and_non_spatial_actually_use(filename): lower = unyt.unyt_quantity(1e5, "Msun") upper = unyt.unyt_quantity(1e13, "Msun") - this_mask.constrain_mask("spherical_overdensity_200_mean", "total_mass", lower, upper) + this_mask.constrain_mask( + "spherical_overdensity_200_mean", "total_mass", lower, upper + ) data = load(filename, mask=this_mask) From 61b328c4b914778bcd33363bf102255c8e6a7812 Mon Sep 17 00:00:00 2001 From: Josh Borrow Date: Fri, 20 Sep 2024 09:50:28 +0200 Subject: [PATCH 14/14] Version revision --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8407d060..1f2e40cd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ packages = [ [project] name = "swiftsimio" -version="9.0.0" +version="9.0.1" authors = [ { name="Josh Borrow", email="josh@joshborrow.com" }, ]