diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 9854204a..3e6372eb 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -20,7 +20,7 @@ honeycomb.workspace = true rayon.workspace = true [dev-dependencies] -honeycomb-core = { workspace = true, features = ["utils"] } +honeycomb-core.workspace = true criterion = { workspace = true, features = ["html_reports"] } iai-callgrind.workspace = true rand = { workspace = true, features = ["small_rng"] } diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 9644836b..573dfb06 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -11,7 +11,7 @@ publish = false autoexamples = false [dev-dependencies] -honeycomb-core = { workspace = true, features = ["utils", "io"] } +honeycomb-core = { workspace = true, features = ["io"] } honeycomb-render = { workspace = true } honeycomb-kernels = { workspace = true } rand = { workspace = true, features = ["small_rng"] } @@ -33,12 +33,6 @@ path = "examples/io/read.rs" name = "io_write" path = "examples/io/write.rs" -# memory usage - -[[example]] -name = "memory_usage" -path = "examples/memory_usage/compute.rs" - # parallelization [[example]] diff --git a/examples/examples/memory_usage/compute.rs b/examples/examples/memory_usage/compute.rs deleted file mode 100644 index 45b03631..00000000 --- a/examples/examples/memory_usage/compute.rs +++ /dev/null @@ -1,27 +0,0 @@ -use honeycomb_core::prelude::{CMap2, CMapBuilder, DartIdType}; - -pub fn main() { - // create a 3x3 grid & remove the central square - let mut cmap: CMap2 = CMapBuilder::unit_grid(3).build().unwrap(); - // darts making up the central square - let (d1, d2, d3, d4): (DartIdType, DartIdType, DartIdType, DartIdType) = (17, 18, 19, 20); - // separate the square from the rest - cmap.force_two_unsew(d1); - cmap.force_two_unsew(d2); - cmap.force_two_unsew(d3); - cmap.force_two_unsew(d4); - // separate dart individually - cmap.force_one_unsew(d1); - cmap.force_one_unsew(d2); - cmap.force_one_unsew(d3); - cmap.force_one_unsew(d4); - // remove darts - cmap.remove_free_dart(d1); - cmap.remove_free_dart(d2); - cmap.remove_free_dart(d3); - cmap.remove_free_dart(d4); - // dump memory usage - cmap.used_size("memusage_example").unwrap(); - cmap.allocated_size("memusage_example").unwrap(); - cmap.effective_size("memusage_example").unwrap(); -} diff --git a/examples/examples/memory_usage/plot.py b/examples/examples/memory_usage/plot.py deleted file mode 100644 index e8f5cacd..00000000 --- a/examples/examples/memory_usage/plot.py +++ /dev/null @@ -1,259 +0,0 @@ -import csv -import matplotlib.pyplot as plt -import sys - - -# Parse command line input -def parseCommandLine(): - opts = [opt for opt in sys.argv[1:] if opt.startswith("-")] - args = [arg for arg in sys.argv[1:] if not arg.startswith("-")] - - if len(args) > 1: - print("E: Multiple arguments provided when only one is needed") - print("Usage: $ python3 plot_memory_usage.py ") - exit(-1) - - if len(args) == 0: - print("E: No input file specified") - print("Usage: $ python3 plot_memory_usage.py ") - exit(-2) - - filename = args[0] - - show = False - overview = False - detailed = False - all_cats = False - - if ".csv" not in filename: - print("W: Specified file may not have the correct format") - - if "--help" in opts or "-h" in opts: # --help / -h -- prints a help message - print("Usage: $ python3 plot_memory_usage.py ") - print("Available options:") - print(" --help -h -- prints this message") - print(" --overview -o -- generates a plot exclusively using total category values") - print(" --detailed -d -- generates a plot using all values of each category") - print(" --all -a -- generates a zoomed-in plot for each category") - exit(0) - - if "--show" in opts: - show = True - if "--overview" in opts or "-o" in opts: - overview = True - if "--detailed" in opts or "-d" in opts: - detailed = True - if "--all" in opts or "-a" in opts: - all_cats = True - - return (filename, show, overview, detailed, all_cats) - - -# Parse file input -def parseDataFromFile(filename: str): - beta = [] - geometry = [] - others = [] - totals = [] - - with open(filename, newline='') as csvfile: - rdr = csv.reader(csvfile, delimiter=',') - for row in rdr: - print(row) - if "beta_" in row[0]: - if "total" in row[0]: - totals.append(int(row[1])) - else: - beta.append(int(row[1])) - elif "geometry_" in row[0]: - if "total" in row[0]: - totals.append(int(row[1])) - else: - geometry.append(int(row[1])) - elif "others" in row[0]: - if "total" in row[0]: - totals.append(int(row[1])) - else: - others.append(int(row[1])) - return (beta, geometry, others, totals) - - -# Main code -def run(): - filename, show, overview, detailed, all_cats = parseCommandLine() - beta, geometry, others, totals = parseDataFromFile(filename) - - category_labels = ["Beta", "Attributes", "Others"] - beta_labels = ["β0", "β1", "β2"] - geometry_labels = ["vertex"] - others_labels = ["freedarts", "counters"] - explode = [0.02, 0.02, 0.02] - - save_file = filename[0:-4] - - if overview: - ofig, oax = plt.subplots() - oax.pie(totals, - explode=explode, - wedgeprops={"edgecolor": "white"}, - autopct='%1.1f%%') - plt.legend( - title="Categories", - ncol=1, - labels=category_labels, - loc="center right", - bbox_to_anchor=(1.3, 0.5), - draggable=True) - plt.title("Memory Usage: Overview") - if show: - plt.show() - else: - plt.savefig(save_file + "_overview.svg") - - if detailed: - dfig, dax = plt.subplots() - - size = 0.3 - vals = beta + geometry + others - - cmap = plt.colormaps["tab20c"] - outer_colors = cmap([0, 4, 8]) - inner_colors = cmap([1, 2, 3, 5, 9, 10]) - - dax.pie(totals, - radius=1, - colors=outer_colors, - autopct='%1.1f%%', - pctdistance=1.25, - explode=explode, - wedgeprops=dict(width=size, edgecolor='w')) - - dax.pie(vals, - radius=1 - size, - colors=inner_colors, - labels=beta_labels + geometry_labels + others_labels, - labeldistance=.65, - textprops={'size': 'xx-small'}, - explode=[0.02, 0.02, 0.02, 0.02, 0.02, 0.02], - wedgeprops=dict(width=size, edgecolor='w')) - - plt.title("Memory Usage: Detailed") - plt.legend( - title="Categories", - ncol=1, - labels=category_labels, - loc="center right", - bbox_to_anchor=(1.3, 0.5), - draggable=True) - if show: - plt.show() - else: - plt.savefig(save_file + "_detailed.svg") - - if all_cats: - cmap = plt.colormaps["tab20c"] - pie_colors = cmap([0, 4, 8, 12]) - beta_colors = cmap([1, 2, 3]) - geometry_colors = cmap([5]) - others_colors = cmap([9, 10]) - - # --- beta chart --- - beta_afig, (beta_aax1, beta_aax2) = plt.subplots(1, 2, figsize=(9, 5)) - beta_afig.subplots_adjust(wspace=0) - - beta_aax1.pie(totals, - labels=category_labels, - explode=explode, - colors=pie_colors, - wedgeprops={"edgecolor": "white"}, - autopct='%1.1f%%') - plt.title("Memory Usage: Beta functions") - - beta_ratios = [b / totals[0] for b in beta] - bottom = 1 - width = .2 - - for j, (height, label) in enumerate(reversed([*zip(beta_ratios, beta_labels)])): - bottom -= height - bc = beta_aax2.bar(0, height, width, bottom=bottom, color=beta_colors, label=label, - alpha=0.1 + 0.25 * j) - beta_aax2.bar_label(bc, labels=[f"{height:.0%}"], label_type='center') - - beta_aax2.set_title("Beta functions") - beta_aax2.legend() - beta_aax2.axis('off') - beta_aax2.set_xlim(- 2.5 * width, 2.5 * width) - - if show: - plt.show() - else: - plt.savefig(save_file + "_beta.svg") - - # --- geometry chart --- - geometry_afig, (geometry_aax1, geometry_aax2) = plt.subplots(1, 2, figsize=(9, 5)) - geometry_afig.subplots_adjust(wspace=0) - - geometry_aax1.pie(totals, - labels=category_labels, - explode=explode, - colors=pie_colors, - wedgeprops={"edgecolor": "white"}, - autopct='%1.1f%%') - plt.title("Memory Usage: Attributes") - - geometry_ratios = [g / totals[1] for g in geometry] - bottom = 1 - width = .2 - - for j, (height, label) in enumerate(reversed([*zip(geometry_ratios, geometry_labels)])): - bottom -= height - bc = geometry_aax2.bar(0, height, width, bottom=bottom, color=geometry_colors, label=label, - alpha=0.1 + 0.25 * j) - geometry_aax2.bar_label(bc, labels=[f"{height:.0%}"], label_type='center') - - geometry_aax2.set_title("Attributes") - geometry_aax2.legend() - geometry_aax2.axis('off') - geometry_aax2.set_xlim(- 2.5 * width, 2.5 * width) - - if show: - plt.show() - else: - plt.savefig(save_file + "_attributes.svg") - - # --- others chart --- - others_afig, (others_aax1, others_aax2) = plt.subplots(1, 2, figsize=(9, 5)) - others_afig.subplots_adjust(wspace=0) - - others_aax1.pie(totals, - labels=category_labels, - explode=explode, - colors=pie_colors, - wedgeprops={"edgecolor": "white"}, - autopct='%1.1f%%') - plt.title("Memory Usage: Miscellaneous data") - - others_ratios = [o / totals[2] for o in others] - bottom = 1 - width = .2 - - for j, (height, label) in enumerate(reversed([*zip(others_ratios, others_labels)])): - bottom -= height - bc = others_aax2.bar(0, height, width, bottom=bottom, color=others_colors, label=label, - alpha=0.1 + 0.25 * j) - others_aax2.bar_label(bc, labels=[f"{height:.0%}"], label_type='center') - - others_aax2.set_title("Miscellaneous data") - others_aax2.legend() - others_aax2.axis('off') - others_aax2.set_xlim(- 2.5 * width, 2.5 * width) - - if show: - plt.show() - else: - plt.savefig(save_file + "_others.svg") - - -# Main -if __name__ == "__main__": - run() diff --git a/honeycomb-core/Cargo.toml b/honeycomb-core/Cargo.toml index 6fc2d111..b06939f7 100644 --- a/honeycomb-core/Cargo.toml +++ b/honeycomb-core/Cargo.toml @@ -14,7 +14,6 @@ publish = true [features] io = ["dep:vtkio"] -utils = [] # deps diff --git a/honeycomb-core/src/attributes/collections.rs b/honeycomb-core/src/attributes/collections.rs index 36843c22..22df8172 100644 --- a/honeycomb-core/src/attributes/collections.rs +++ b/honeycomb-core/src/attributes/collections.rs @@ -204,24 +204,3 @@ impl AttributeStorage for AttrSparseVec AttrSparseVec { - /// Return the amount of space allocated for the storage. - #[must_use = "returned value is not used, consider removing this method call"] - pub fn allocated_size(&self) -> usize { - self.data.capacity() * std::mem::size_of::>() - } - - /// Return the total amount of space used by the storage. - #[must_use = "returned value is not used, consider removing this method call"] - pub fn effective_size(&self) -> usize { - self.data.len() * std::mem::size_of::>() - } - - /// Return the amount of space used by valid entries of the storage. - #[must_use = "returned value is not used, consider removing this method call"] - pub fn used_size(&self) -> usize { - self.n_attributes() * size_of::>>() - } -} diff --git a/honeycomb-core/src/cmap/builder/grid/building_routines.rs b/honeycomb-core/src/cmap/builder/grid.rs similarity index 58% rename from honeycomb-core/src/cmap/builder/grid/building_routines.rs rename to honeycomb-core/src/cmap/builder/grid.rs index c1c9179b..42e910bb 100644 --- a/honeycomb-core/src/cmap/builder/grid/building_routines.rs +++ b/honeycomb-core/src/cmap/builder/grid.rs @@ -1,19 +1,167 @@ -//! Internal grid-building routines -//! -//! Grids are built from left to right (ascending X), from bottom to top (ascending Y). We rely on -//! this logic to compute the value of each beta function entry, as well as bind orbits to vertices. -//! -//! See [`CMapBuilder::unit_grid`] and [`CMapBuilder::unit_triangles`] documentation entries. +use crate::prelude::{BuilderError, CMap2, DartIdType, Vector2, Vertex2}; +use crate::{attributes::AttrStorageManager, geometry::CoordsFloat}; -// ------ IMPORTS +// --- grid descriptor -#[cfg(doc)] -use crate::prelude::CMapBuilder; +/// Grid description used to generate maps via the map builder. +/// +/// The user must specify two out of these three characteristics: +/// +/// - `n_cells: [usize; 3]` -- The number of cells per axis +/// - `len_per_cell: [T; 3]` -- The dimensions of cells per axis +/// - `lens: [T; 3]` -- The dimensions of the grid per axis +/// +/// # Generics +/// +/// - `T: CoordsFloat` -- Generic type of the future map object. +#[derive(Default, Clone)] +pub struct GridDescriptor { + pub(crate) origin: Vertex2, + pub(crate) n_cells: Option<[usize; 3]>, + pub(crate) len_per_cell: Option<[T; 3]>, + pub(crate) lens: Option<[T; 3]>, + pub(crate) split_quads: bool, +} -use crate::prelude::{CMap2, DartIdType, Vector2, Vertex2}; -use crate::{attributes::AttrStorageManager, geometry::CoordsFloat}; +macro_rules! setters { + ($fld: ident, $fldx: ident, $fldy: ident, $fldz: ident, $zero: expr, $fldty: ty) => { + /// Set values for all dimensions + #[must_use = "unused builder object, consider removing this method call"] + pub fn $fld(mut self, $fld: [$fldty; 3]) -> Self { + self.$fld = Some($fld); + self + } + + /// Set x-axis value + #[must_use = "unused builder object, consider removing this method call"] + pub fn $fldx(mut self, $fld: $fldty) -> Self { + if let Some([ptr, _, _]) = &mut self.$fld { + *ptr = $fld; + } else { + self.$fld = Some([$fld, $zero, $zero]); + } + self + } + + /// Set y-axis value + #[must_use = "unused builder object, consider removing this method call"] + pub fn $fldy(mut self, $fld: $fldty) -> Self { + if let Some([_, ptr, _]) = &mut self.$fld { + *ptr = $fld; + } else { + self.$fld = Some([$zero, $fld, $zero]); + } + self + } + + /// Set z-axis value + #[must_use = "unused builder object, consider removing this method call"] + pub fn $fldz(mut self, $fld: $fldty) -> Self { + if let Some([_, _, ptr]) = &mut self.$fld { + *ptr = $fld; + } else { + self.$fld = Some([$zero, $zero, $fld]); + } + self + } + }; +} + +impl GridDescriptor { + // n_cells + setters!(n_cells, n_cells_x, n_cells_y, n_cells_z, 0, usize); + + // len_per_cell + setters!( + len_per_cell, + len_per_cell_x, + len_per_cell_y, + len_per_cell_z, + T::zero(), + T + ); + + // lens + setters!(lens, lens_x, lens_y, lens_z, T::zero(), T); + + /// Set origin (most bottom-left vertex) of the grid + #[must_use = "unused builder object, consider removing this method call"] + pub fn origin(mut self, origin: Vertex2) -> Self { + self.origin = origin; + self + } + + /// Indicate whether to split quads of the grid + #[must_use = "unused builder object, consider removing this method call"] + pub fn split_quads(mut self, split: bool) -> Self { + self.split_quads = split; + self + } +} + +// --- parsing routine + +macro_rules! check_parameters { + ($id: ident, $msg: expr) => { + if $id.is_sign_negative() | $id.is_zero() { + return Err(BuilderError::InvalidGridParameters($msg)); + } + }; +} + +impl GridDescriptor { + /// Parse provided grid parameters to provide what's used to actually generate the grid. + #[allow(clippy::type_complexity)] + pub(crate) fn parse_2d(self) -> Result<(Vertex2, [usize; 2], [T; 2]), BuilderError> { + match (self.n_cells, self.len_per_cell, self.lens) { + // from # cells and lengths per cell + (Some([nx, ny, _]), Some([lpx, lpy, _]), lens) => { + if lens.is_some() { + eprintln!("W: All three grid parameters were specified, total lengths will be ignored"); + } + #[rustfmt::skip] + check_parameters!(lpx, "length per x cell is null or negative"); + #[rustfmt::skip] + check_parameters!(lpy, "length per y cell is null or negative"); + Ok((self.origin, [nx, ny], [lpx, lpy])) + } + // from # cells and total lengths + (Some([nx, ny, _]), None, Some([lx, ly, _])) => { + #[rustfmt::skip] + check_parameters!(lx, "grid length along x is null or negative"); + #[rustfmt::skip] + check_parameters!(ly, "grid length along y is null or negative"); + Ok(( + self.origin, + [nx, ny], + [lx / T::from(nx).unwrap(), ly / T::from(ny).unwrap()], + )) + } + // from lengths per cell and total lengths + (None, Some([lpx, lpy, _]), Some([lx, ly, _])) => { + #[rustfmt::skip] + check_parameters!(lpx, "length per x cell is null or negative"); + #[rustfmt::skip] + check_parameters!(lpy, "length per y cell is null or negative"); + #[rustfmt::skip] + check_parameters!(lx, "grid length along x is null or negative"); + #[rustfmt::skip] + check_parameters!(ly, "grid length along y is null or negative"); + Ok(( + self.origin, + [ + (lx / lpx).ceil().to_usize().unwrap(), + (ly / lpy).ceil().to_usize().unwrap(), + ], + [lpx, lpy], + )) + } + (_, _, _) => Err(BuilderError::MissingGridParameters), + } + } +} -// ------ CONTENT +// --- building routines /// Internal grid-building routine #[allow(clippy::too_many_lines)] diff --git a/honeycomb-core/src/cmap/builder/grid/descriptor.rs b/honeycomb-core/src/cmap/builder/grid/descriptor.rs deleted file mode 100644 index 91a940c5..00000000 --- a/honeycomb-core/src/cmap/builder/grid/descriptor.rs +++ /dev/null @@ -1,171 +0,0 @@ -//! Main grid descriptor implementation - -// ------ IMPORTS - -use crate::geometry::CoordsFloat; -use crate::prelude::{BuilderError, Vertex2}; - -#[cfg(doc)] -use crate::prelude::CMapBuilder; - -// ------ CONTENT - -/// Grid description used to generate maps via the map builder. -/// -/// The user must specify two out of these three characteristics: -/// -/// - `n_cells: [usize; 3]` -- The number of cells per axis -/// - `len_per_cell: [T; 3]` -- The dimensions of cells per axis -/// - `lens: [T; 3]` -- The dimensions of the grid per axis -/// -/// # Generics -/// -/// - `T: CoordsFloat` -- Generic type of the future map object. -#[derive(Default, Clone)] -pub struct GridDescriptor { - pub(crate) origin: Vertex2, - pub(crate) n_cells: Option<[usize; 3]>, - pub(crate) len_per_cell: Option<[T; 3]>, - pub(crate) lens: Option<[T; 3]>, - pub(crate) split_quads: bool, -} - -// --- setters - -macro_rules! setters { - ($fld: ident, $fldx: ident, $fldy: ident, $fldz: ident, $zero: expr, $fldty: ty) => { - /// Set values for all dimensions - #[must_use = "unused builder object, consider removing this method call"] - pub fn $fld(mut self, $fld: [$fldty; 3]) -> Self { - self.$fld = Some($fld); - self - } - - /// Set x-axis value - #[must_use = "unused builder object, consider removing this method call"] - pub fn $fldx(mut self, $fld: $fldty) -> Self { - if let Some([ptr, _, _]) = &mut self.$fld { - *ptr = $fld; - } else { - self.$fld = Some([$fld, $zero, $zero]); - } - self - } - - /// Set y-axis value - #[must_use = "unused builder object, consider removing this method call"] - pub fn $fldy(mut self, $fld: $fldty) -> Self { - if let Some([_, ptr, _]) = &mut self.$fld { - *ptr = $fld; - } else { - self.$fld = Some([$zero, $fld, $zero]); - } - self - } - - /// Set z-axis value - #[must_use = "unused builder object, consider removing this method call"] - pub fn $fldz(mut self, $fld: $fldty) -> Self { - if let Some([_, _, ptr]) = &mut self.$fld { - *ptr = $fld; - } else { - self.$fld = Some([$zero, $zero, $fld]); - } - self - } - }; -} - -impl GridDescriptor { - // n_cells - setters!(n_cells, n_cells_x, n_cells_y, n_cells_z, 0, usize); - - // len_per_cell - setters!( - len_per_cell, - len_per_cell_x, - len_per_cell_y, - len_per_cell_z, - T::zero(), - T - ); - - // lens - setters!(lens, lens_x, lens_y, lens_z, T::zero(), T); - - /// Set origin (most bottom-left vertex) of the grid - #[must_use = "unused builder object, consider removing this method call"] - pub fn origin(mut self, origin: Vertex2) -> Self { - self.origin = origin; - self - } - - /// Indicate whether to split quads of the grid - #[must_use = "unused builder object, consider removing this method call"] - pub fn split_quads(mut self, split: bool) -> Self { - self.split_quads = split; - self - } -} - -// --- parsing routine - -macro_rules! check_parameters { - ($id: ident, $msg: expr) => { - if $id.is_sign_negative() | $id.is_zero() { - return Err(BuilderError::InvalidGridParameters($msg)); - } - }; -} - -impl GridDescriptor { - /// Parse provided grid parameters to provide what's used to actually generate the grid. - #[allow(clippy::type_complexity)] - pub(crate) fn parse_2d(self) -> Result<(Vertex2, [usize; 2], [T; 2]), BuilderError> { - match (self.n_cells, self.len_per_cell, self.lens) { - // from # cells and lengths per cell - (Some([nx, ny, _]), Some([lpx, lpy, _]), lens) => { - if lens.is_some() { - eprintln!("W: All three grid parameters were specified, total lengths will be ignored"); - } - #[rustfmt::skip] - check_parameters!(lpx, "length per x cell is null or negative"); - #[rustfmt::skip] - check_parameters!(lpy, "length per y cell is null or negative"); - Ok((self.origin, [nx, ny], [lpx, lpy])) - } - // from # cells and total lengths - (Some([nx, ny, _]), None, Some([lx, ly, _])) => { - #[rustfmt::skip] - check_parameters!(lx, "grid length along x is null or negative"); - #[rustfmt::skip] - check_parameters!(ly, "grid length along y is null or negative"); - Ok(( - self.origin, - [nx, ny], - [lx / T::from(nx).unwrap(), ly / T::from(ny).unwrap()], - )) - } - // from lengths per cell and total lengths - (None, Some([lpx, lpy, _]), Some([lx, ly, _])) => { - #[rustfmt::skip] - check_parameters!(lpx, "length per x cell is null or negative"); - #[rustfmt::skip] - check_parameters!(lpy, "length per y cell is null or negative"); - #[rustfmt::skip] - check_parameters!(lx, "grid length along x is null or negative"); - #[rustfmt::skip] - check_parameters!(ly, "grid length along y is null or negative"); - Ok(( - self.origin, - [ - (lx / lpx).ceil().to_usize().unwrap(), - (ly / lpy).ceil().to_usize().unwrap(), - ], - [lpx, lpy], - )) - } - (_, _, _) => Err(BuilderError::MissingGridParameters), - } - } -} diff --git a/honeycomb-core/src/cmap/builder/grid/mod.rs b/honeycomb-core/src/cmap/builder/grid/mod.rs deleted file mode 100644 index dab33e64..00000000 --- a/honeycomb-core/src/cmap/builder/grid/mod.rs +++ /dev/null @@ -1,106 +0,0 @@ -//! Utility for sample map generation -//! -//! This module contains code used to generate maps that represent grids. These have a variety -//! of usages, most notably in tests, benchmarks, and specific algorithms. - -// ------ MODULE DECLARATIONS - -pub mod building_routines; -pub mod descriptor; - -// ------ RE-EXPORTS - -use crate::geometry::CoordsFloat; -use crate::prelude::{CMapBuilder, GridDescriptor}; - -// ------ CONTENT - -// --- impl items for CMapBuilder - -impl CMapBuilder { - #[cfg(feature = "utils")] - /// Set the [`GridDescriptor`] that will be used when building the map. - #[must_use = "unused builder object, consider removing this method call"] - pub fn grid_descriptor(mut self, grid_descriptor: GridDescriptor) -> Self { - self.grid_descriptor = Some(grid_descriptor); - self - } -} -/// Create a [`CMapBuilder`] from a [`GridDescriptor`]. -/// -/// This implementation is roughly equivalent to the following: -/// -/// ```rust -/// # use honeycomb_core::prelude::{CMapBuilder, GridDescriptor}; -/// // setup grid descriptor -/// let gridd = GridDescriptor::default(); -/// // ... -/// -/// // `CMapBuilder::from(gridd)`, or: -/// let builder = CMapBuilder::::default().grid_descriptor(gridd); -/// ``` -#[cfg(feature = "utils")] -impl From> for CMapBuilder { - fn from(value: GridDescriptor) -> Self { - CMapBuilder { - grid_descriptor: Some(value), - ..Default::default() - } - } -} - -// --- predefinite grid setups for CMapBuilder - -impl CMapBuilder { - /// Create a [`CMapBuilder`] with a predefinite [`GridDescriptor`] value. - /// - /// # Arguments - /// - /// - `n_square: usize` -- Number of cells along each axis. - /// - /// # Return - /// - /// This function return a builder structure with predefinite parameters to generate - /// a specific map. - /// - /// The map generated by this predefinite value corresponds to an orthogonal mesh, with an - /// equal number of cells along each axis. - /// - /// ![`CMAP2_GRID`](https://lihpc-computational-geometry.github.io/honeycomb/images/bg_grid.svg) - #[must_use = "unused builder object, consider removing this function call"] - pub fn unit_grid(n_square: usize) -> Self { - GridDescriptor::default() - .n_cells([n_square; 3]) - .len_per_cell([T::one(); 3]) - .into() - } - - /// Create a [`CMapBuilder`] with a predefinite [`GridDescriptor`] value. - /// - /// # Arguments - /// - /// - `n_square: usize` -- Number of cells along each axis. - /// - /// # Return - /// - /// This function return a builder structure with predefinite parameters to generate - /// a specific map. - /// - /// The map generated by this predefinite value corresponds to an orthogonal mesh, with an - /// equal number of cells along each axis. Each cell will be split across their diagonal (top - /// left to bottom right) to form triangles. - /// - /// ![`CMAP2_GRID`](https://lihpc-computational-geometry.github.io/honeycomb/images/bg_grid_tri.svg) - #[must_use = "unused builder object, consider removing this function call"] - pub fn unit_triangles(n_square: usize) -> Self { - GridDescriptor::default() - .n_cells([n_square; 3]) - .len_per_cell([T::one(); 3]) - .split_quads(true) - .into() - } -} - -// ------ TESTS -#[cfg(test)] -mod tests; diff --git a/honeycomb-core/src/cmap/builder/grid/tests.rs b/honeycomb-core/src/cmap/builder/grid/tests.rs deleted file mode 100644 index 5fc9baad..00000000 --- a/honeycomb-core/src/cmap/builder/grid/tests.rs +++ /dev/null @@ -1,354 +0,0 @@ -// ------ IMPORTS - -use crate::prelude::{CMap2, CMapBuilder, GridDescriptor}; - -// ------ CONTENT - -// --- descriptor - -#[test] -fn build_nc_lpc_l() { - let descriptor = GridDescriptor::default() - .n_cells([4, 4, 0]) - .len_per_cell([1.0_f64, 1.0_f64, 1.0_f64]) - .lens([4.0_f64, 4.0_f64, 4.0_f64]); - assert!(descriptor.clone().parse_2d().is_ok()); - assert!(descriptor.split_quads(true).parse_2d().is_ok()); -} - -#[test] -fn build_nc_lpc() { - let descriptor = GridDescriptor::default() - .n_cells([4, 4, 0]) - .len_per_cell([1.0_f64, 1.0_f64, 1.0_f64]); - assert!(descriptor.clone().parse_2d().is_ok()); - assert!(descriptor.split_quads(true).parse_2d().is_ok()); -} - -#[test] -fn build_nc_l() { - let descriptor = GridDescriptor::default() - .n_cells_x(4) - .n_cells_y(4) - .lens([4.0_f64, 4.0_f64, 4.0_f64]); - assert!(descriptor.clone().parse_2d().is_ok()); - assert!(descriptor.split_quads(true).parse_2d().is_ok()); -} - -#[test] -fn build_lpc_l() { - let descriptor = GridDescriptor::default() - .len_per_cell_x(1.0_f64) - .len_per_cell_y(1.0_f64) - .lens_x(4.0) - .lens_y(4.0); - assert!(descriptor.clone().parse_2d().is_ok()); - assert!(descriptor.split_quads(true).parse_2d().is_ok()); -} - -#[test] -fn build_incomplete() { - assert!(GridDescriptor::default() - .len_per_cell([1.0_f64, 1.0_f64, 1.0_f64]) - .parse_2d() - .is_err()); - assert!(>::default() - .n_cells([4, 4, 0]) - .parse_2d() - .is_err()); - assert!(GridDescriptor::default() - .lens([4.0_f64, 4.0_f64, 4.0_f64]) - .parse_2d() - .is_err()); -} - -#[test] -#[should_panic(expected = "length per y cell is null or negative")] -fn build_neg_lpc() { - let tmp = GridDescriptor::default() - .n_cells([4, 4, 0]) - .len_per_cell([1.0_f64, -1.0_f64, 1.0_f64]) - .parse_2d(); - let _ = tmp.unwrap(); // panic on Err(BuilderError::InvalidParameters) -} - -#[test] -#[should_panic(expected = "grid length along x is null or negative")] -fn build_null_l() { - let tmp = GridDescriptor::default() - .n_cells([4, 4, 0]) - .lens([0.0_f64, 4.0_f64, 4.0_f64]) - .parse_2d(); - let _ = tmp.unwrap(); // panic on Err(BuilderError::InvalidParameters) -} - -#[test] -#[should_panic(expected = "length per x cell is null or negative")] -fn build_neg_lpc_neg_l() { - // lpc are parsed first so their panic msg should be the one to pop - // x val is parsed first so ... - let tmp = GridDescriptor::default() - .len_per_cell([-1.0_f64, -1.0_f64, 1.0_f64]) - .lens([0.0_f64, 4.0_f64, 4.0_f64]) - .parse_2d(); - let _ = tmp.unwrap(); // panic on Err(BuilderError::InvalidParameters) -} - -// --- grid building - -#[test] -fn square_cmap2_correctness() { - let descriptor = GridDescriptor::default() - .n_cells([2, 2, 2]) - .len_per_cell([1., 1., 1.]); - let cmap: CMap2 = CMapBuilder::from(descriptor).build().unwrap(); - - // hardcoded because using a generic loop & dim would just mean - // reusing the same pattern as the one used during construction - - // face 0 - assert_eq!(cmap.face_id(1), 1); - assert_eq!(cmap.face_id(2), 1); - assert_eq!(cmap.face_id(3), 1); - assert_eq!(cmap.face_id(4), 1); - - // i-cell uses beta 0 to ensure correctness, so the iterator is BFS-like - let mut face = cmap.i_cell::<2>(1); - assert_eq!(face.next(), Some(1)); - assert_eq!(face.next(), Some(2)); // b1 - assert_eq!(face.next(), Some(4)); // b0 - assert_eq!(face.next(), Some(3)); // b1b1 - assert_eq!(face.next(), None); - - assert_eq!(cmap.beta::<1>(1), 2); - assert_eq!(cmap.beta::<1>(2), 3); - assert_eq!(cmap.beta::<1>(3), 4); - assert_eq!(cmap.beta::<1>(4), 1); - - assert_eq!(cmap.beta::<2>(1), 0); - assert_eq!(cmap.beta::<2>(2), 8); - assert_eq!(cmap.beta::<2>(3), 9); - assert_eq!(cmap.beta::<2>(4), 0); - - // face 1 - assert_eq!(cmap.face_id(5), 5); - assert_eq!(cmap.face_id(6), 5); - assert_eq!(cmap.face_id(7), 5); - assert_eq!(cmap.face_id(8), 5); - - let mut face = cmap.i_cell::<2>(5); - assert_eq!(face.next(), Some(5)); - assert_eq!(face.next(), Some(6)); - assert_eq!(face.next(), Some(8)); - assert_eq!(face.next(), Some(7)); - assert_eq!(face.next(), None); - - assert_eq!(cmap.beta::<1>(5), 6); - assert_eq!(cmap.beta::<1>(6), 7); - assert_eq!(cmap.beta::<1>(7), 8); - assert_eq!(cmap.beta::<1>(8), 5); - - assert_eq!(cmap.beta::<2>(5), 0); - assert_eq!(cmap.beta::<2>(6), 0); - assert_eq!(cmap.beta::<2>(7), 13); - assert_eq!(cmap.beta::<2>(8), 2); - - // face 2 - assert_eq!(cmap.face_id(9), 9); - assert_eq!(cmap.face_id(10), 9); - assert_eq!(cmap.face_id(11), 9); - assert_eq!(cmap.face_id(12), 9); - - let mut face = cmap.i_cell::<2>(9); - assert_eq!(face.next(), Some(9)); - assert_eq!(face.next(), Some(10)); - assert_eq!(face.next(), Some(12)); - assert_eq!(face.next(), Some(11)); - assert_eq!(face.next(), None); - - assert_eq!(cmap.beta::<1>(9), 10); - assert_eq!(cmap.beta::<1>(10), 11); - assert_eq!(cmap.beta::<1>(11), 12); - assert_eq!(cmap.beta::<1>(12), 9); - - assert_eq!(cmap.beta::<2>(9), 3); - assert_eq!(cmap.beta::<2>(10), 16); - assert_eq!(cmap.beta::<2>(11), 0); - assert_eq!(cmap.beta::<2>(12), 0); - - // face 3 - assert_eq!(cmap.face_id(13), 13); - assert_eq!(cmap.face_id(14), 13); - assert_eq!(cmap.face_id(15), 13); - assert_eq!(cmap.face_id(16), 13); - - let mut face = cmap.i_cell::<2>(13); - assert_eq!(face.next(), Some(13)); - assert_eq!(face.next(), Some(14)); - assert_eq!(face.next(), Some(16)); - assert_eq!(face.next(), Some(15)); - assert_eq!(face.next(), None); - - assert_eq!(cmap.beta::<1>(13), 14); - assert_eq!(cmap.beta::<1>(14), 15); - assert_eq!(cmap.beta::<1>(15), 16); - assert_eq!(cmap.beta::<1>(16), 13); - - assert_eq!(cmap.beta::<2>(13), 7); - assert_eq!(cmap.beta::<2>(14), 0); - assert_eq!(cmap.beta::<2>(15), 0); - assert_eq!(cmap.beta::<2>(16), 10); -} - -#[allow(clippy::too_many_lines)] -#[test] -fn splitsquare_cmap2_correctness() { - let cmap: CMap2 = CMapBuilder::unit_triangles(2).build().unwrap(); - - // hardcoded because using a generic loop & dim would just mean - // reusing the same pattern as the one used during construction - - // face 1 - assert_eq!(cmap.face_id(1), 1); - assert_eq!(cmap.face_id(2), 1); - assert_eq!(cmap.face_id(3), 1); - - let mut face = cmap.i_cell::<2>(1); - assert_eq!(face.next(), Some(1)); - assert_eq!(face.next(), Some(2)); - assert_eq!(face.next(), Some(3)); - - assert_eq!(cmap.beta::<1>(1), 2); - assert_eq!(cmap.beta::<1>(2), 3); - assert_eq!(cmap.beta::<1>(3), 1); - - assert_eq!(cmap.beta::<2>(1), 0); - assert_eq!(cmap.beta::<2>(2), 4); - assert_eq!(cmap.beta::<2>(3), 0); - - // face 4 - assert_eq!(cmap.face_id(4), 4); - assert_eq!(cmap.face_id(5), 4); - assert_eq!(cmap.face_id(6), 4); - - let mut face = cmap.i_cell::<2>(4); - assert_eq!(face.next(), Some(4)); - assert_eq!(face.next(), Some(5)); - assert_eq!(face.next(), Some(6)); - - assert_eq!(cmap.beta::<1>(4), 5); - assert_eq!(cmap.beta::<1>(5), 6); - assert_eq!(cmap.beta::<1>(6), 4); - - assert_eq!(cmap.beta::<2>(4), 2); - assert_eq!(cmap.beta::<2>(5), 9); - assert_eq!(cmap.beta::<2>(6), 13); - - // face 7 - assert_eq!(cmap.face_id(7), 7); - assert_eq!(cmap.face_id(8), 7); - assert_eq!(cmap.face_id(9), 7); - - let mut face = cmap.i_cell::<2>(7); - assert_eq!(face.next(), Some(7)); - assert_eq!(face.next(), Some(8)); - assert_eq!(face.next(), Some(9)); - - assert_eq!(cmap.beta::<1>(7), 8); - assert_eq!(cmap.beta::<1>(8), 9); - assert_eq!(cmap.beta::<1>(9), 7); - - assert_eq!(cmap.beta::<2>(7), 0); - assert_eq!(cmap.beta::<2>(8), 10); - assert_eq!(cmap.beta::<2>(9), 5); - - // face 10 - assert_eq!(cmap.face_id(10), 10); - assert_eq!(cmap.face_id(11), 10); - assert_eq!(cmap.face_id(12), 10); - - let mut face = cmap.i_cell::<2>(10); - assert_eq!(face.next(), Some(10)); - assert_eq!(face.next(), Some(11)); - assert_eq!(face.next(), Some(12)); - - assert_eq!(cmap.beta::<1>(10), 11); - assert_eq!(cmap.beta::<1>(11), 12); - assert_eq!(cmap.beta::<1>(12), 10); - - assert_eq!(cmap.beta::<2>(10), 8); - assert_eq!(cmap.beta::<2>(11), 0); - assert_eq!(cmap.beta::<2>(12), 19); - - // face 13 - assert_eq!(cmap.face_id(13), 13); - assert_eq!(cmap.face_id(14), 13); - assert_eq!(cmap.face_id(15), 13); - - let mut face = cmap.i_cell::<2>(13); - assert_eq!(face.next(), Some(13)); - assert_eq!(face.next(), Some(14)); - assert_eq!(face.next(), Some(15)); - - assert_eq!(cmap.beta::<1>(13), 14); - assert_eq!(cmap.beta::<1>(14), 15); - assert_eq!(cmap.beta::<1>(15), 13); - - assert_eq!(cmap.beta::<2>(13), 6); - assert_eq!(cmap.beta::<2>(14), 16); - assert_eq!(cmap.beta::<2>(15), 0); - - // face 16 - assert_eq!(cmap.face_id(16), 16); - assert_eq!(cmap.face_id(17), 16); - assert_eq!(cmap.face_id(18), 16); - - let mut face = cmap.i_cell::<2>(16); - assert_eq!(face.next(), Some(16)); - assert_eq!(face.next(), Some(17)); - assert_eq!(face.next(), Some(18)); - - assert_eq!(cmap.beta::<1>(16), 17); - assert_eq!(cmap.beta::<1>(17), 18); - assert_eq!(cmap.beta::<1>(18), 16); - - assert_eq!(cmap.beta::<2>(16), 14); - assert_eq!(cmap.beta::<2>(17), 21); - assert_eq!(cmap.beta::<2>(18), 0); - - // face 19 - assert_eq!(cmap.face_id(19), 19); - assert_eq!(cmap.face_id(20), 19); - assert_eq!(cmap.face_id(21), 19); - - let mut face = cmap.i_cell::<2>(19); - assert_eq!(face.next(), Some(19)); - assert_eq!(face.next(), Some(20)); - assert_eq!(face.next(), Some(21)); - - assert_eq!(cmap.beta::<1>(19), 20); - assert_eq!(cmap.beta::<1>(20), 21); - assert_eq!(cmap.beta::<1>(21), 19); - - assert_eq!(cmap.beta::<2>(19), 12); - assert_eq!(cmap.beta::<2>(20), 22); - assert_eq!(cmap.beta::<2>(21), 17); - - // face 22 - assert_eq!(cmap.face_id(22), 22); - assert_eq!(cmap.face_id(23), 22); - assert_eq!(cmap.face_id(24), 22); - - let mut face = cmap.i_cell::<2>(22); - assert_eq!(face.next(), Some(22)); - assert_eq!(face.next(), Some(23)); - assert_eq!(face.next(), Some(24)); - - assert_eq!(cmap.beta::<1>(22), 23); - assert_eq!(cmap.beta::<1>(23), 24); - assert_eq!(cmap.beta::<1>(24), 22); - - assert_eq!(cmap.beta::<2>(22), 20); - assert_eq!(cmap.beta::<2>(23), 0); - assert_eq!(cmap.beta::<2>(24), 0); -} diff --git a/honeycomb-core/src/cmap/builder/mod.rs b/honeycomb-core/src/cmap/builder/mod.rs index 14cbefb9..dd4e59c5 100644 --- a/honeycomb-core/src/cmap/builder/mod.rs +++ b/honeycomb-core/src/cmap/builder/mod.rs @@ -2,16 +2,15 @@ // ------ MODULE DECLARATIONS -#[cfg(feature = "utils")] pub mod grid; +pub mod structure; + #[cfg(feature = "io")] pub mod io; -pub mod structure; // ------ RE-EXPORTS -#[cfg(feature = "utils")] -pub use grid::descriptor::GridDescriptor; +pub use grid::GridDescriptor; pub use structure::{BuilderError, CMapBuilder}; // ------ CONTENT diff --git a/honeycomb-core/src/cmap/builder/structure.rs b/honeycomb-core/src/cmap/builder/structure.rs index 8fbe5c99..a75fba37 100644 --- a/honeycomb-core/src/cmap/builder/structure.rs +++ b/honeycomb-core/src/cmap/builder/structure.rs @@ -1,23 +1,14 @@ -//! Module short description -//! -//! Should you interact with this module directly? -//! -//! Content description if needed - // ------ IMPORTS -#[cfg(feature = "utils")] -use super::GridDescriptor; -use crate::prelude::{AttributeBind, CMap2}; +use crate::prelude::{AttributeBind, CMap2, GridDescriptor}; use crate::{attributes::AttrStorageManager, geometry::CoordsFloat}; + use thiserror::Error; #[cfg(feature = "io")] use vtkio::Vtk; // ------ CONTENT -// --- common error enum - /// Builder-level error enum /// /// This enum is used to describe all non-panic errors that can occur when using a builder @@ -40,8 +31,6 @@ pub enum BuilderError { UnsupportedVtkData(&'static str), } -// --- main struct - /// Combinatorial map builder structure. /// /// # Example @@ -66,13 +55,73 @@ where { #[cfg(feature = "io")] pub(super) vtk_file: Option, - #[cfg(feature = "utils")] pub(super) grid_descriptor: Option>, pub(super) attributes: AttrStorageManager, pub(super) n_darts: usize, pub(super) coordstype: std::marker::PhantomData, } +// --- `From` impls & predefinite values + +impl From> for CMapBuilder { + fn from(value: GridDescriptor) -> Self { + CMapBuilder { + grid_descriptor: Some(value), + ..Default::default() + } + } +} + +impl CMapBuilder { + /// Create a [`CMapBuilder`] with a predefinite [`GridDescriptor`] value. + /// + /// # Arguments + /// + /// - `n_square: usize` -- Number of cells along each axis. + /// + /// # Return + /// + /// This function return a builder structure with predefinite parameters to generate + /// a specific map. + /// + /// The map generated by this predefinite value corresponds to an orthogonal mesh, with an + /// equal number of cells along each axis. + /// + /// ![`CMAP2_GRID`](https://lihpc-computational-geometry.github.io/honeycomb/images/bg_grid.svg) + #[must_use = "unused builder object, consider removing this function call"] + pub fn unit_grid(n_square: usize) -> Self { + GridDescriptor::default() + .n_cells([n_square; 3]) + .len_per_cell([T::one(); 3]) + .into() + } + + /// Create a [`CMapBuilder`] with a predefinite [`GridDescriptor`] value. + /// + /// # Arguments + /// + /// - `n_square: usize` -- Number of cells along each axis. + /// + /// # Return + /// + /// This function return a builder structure with predefinite parameters to generate + /// a specific map. + /// + /// The map generated by this predefinite value corresponds to an orthogonal mesh, with an + /// equal number of cells along each axis. Each cell will be split across their diagonal (top + /// left to bottom right) to form triangles. + /// + /// ![`CMAP2_GRID`](https://lihpc-computational-geometry.github.io/honeycomb/images/bg_grid_tri.svg) + #[must_use = "unused builder object, consider removing this function call"] + pub fn unit_triangles(n_square: usize) -> Self { + GridDescriptor::default() + .n_cells([n_square; 3]) + .len_per_cell([T::one(); 3]) + .split_quads(true) + .into() + } +} + // --- setters impl CMapBuilder { @@ -83,6 +132,13 @@ impl CMapBuilder { self } + /// Set the [`GridDescriptor`] that will be used when building the map. + #[must_use = "unused builder object, consider removing this method call"] + pub fn grid_descriptor(mut self, grid_descriptor: GridDescriptor) -> Self { + self.grid_descriptor = Some(grid_descriptor); + self + } + /// Add the specified attribute that the created map will contain. /// /// Each attribute must be uniquely typed, i.e. a single type or struct cannot be added twice @@ -127,20 +183,14 @@ impl CMapBuilder { // this routine should return a Result instead of the map directly return super::io::build_2d_from_vtk(vfile, self.attributes); } - #[cfg(feature = "utils")] if let Some(gridb) = self.grid_descriptor { // build from grid descriptor let split = gridb.split_quads; return gridb.parse_2d().map(|(origin, ns, lens)| { if split { - super::grid::building_routines::build_2d_splitgrid( - origin, - ns, - lens, - self.attributes, - ) + super::grid::build_2d_splitgrid(origin, ns, lens, self.attributes) } else { - super::grid::building_routines::build_2d_grid(origin, ns, lens, self.attributes) + super::grid::build_2d_grid(origin, ns, lens, self.attributes) } }); } diff --git a/honeycomb-core/src/cmap/builder/tests.rs b/honeycomb-core/src/cmap/builder/tests.rs index 5fc4c9f9..a80e213b 100644 --- a/honeycomb-core/src/cmap/builder/tests.rs +++ b/honeycomb-core/src/cmap/builder/tests.rs @@ -1,8 +1,6 @@ -// ------ IMPORTS +use crate::prelude::{CMap2, CMapBuilder, GridDescriptor}; -use crate::prelude::{CMap2, CMapBuilder}; - -// ------ CONTENT +// --- basic #[test] fn example_test() { @@ -10,3 +8,352 @@ fn example_test() { let cmap: CMap2 = builder.build().unwrap(); assert_eq!(cmap.n_darts(), 11); } + +// --- grid + +#[test] +fn build_nc_lpc_l() { + let descriptor = GridDescriptor::default() + .n_cells([4, 4, 0]) + .len_per_cell([1.0_f64, 1.0_f64, 1.0_f64]) + .lens([4.0_f64, 4.0_f64, 4.0_f64]); + assert!(descriptor.clone().parse_2d().is_ok()); + assert!(descriptor.split_quads(true).parse_2d().is_ok()); +} + +#[test] +fn build_nc_lpc() { + let descriptor = GridDescriptor::default() + .n_cells([4, 4, 0]) + .len_per_cell([1.0_f64, 1.0_f64, 1.0_f64]); + assert!(descriptor.clone().parse_2d().is_ok()); + assert!(descriptor.split_quads(true).parse_2d().is_ok()); +} + +#[test] +fn build_nc_l() { + let descriptor = GridDescriptor::default() + .n_cells_x(4) + .n_cells_y(4) + .lens([4.0_f64, 4.0_f64, 4.0_f64]); + assert!(descriptor.clone().parse_2d().is_ok()); + assert!(descriptor.split_quads(true).parse_2d().is_ok()); +} + +#[test] +fn build_lpc_l() { + let descriptor = GridDescriptor::default() + .len_per_cell_x(1.0_f64) + .len_per_cell_y(1.0_f64) + .lens_x(4.0) + .lens_y(4.0); + assert!(descriptor.clone().parse_2d().is_ok()); + assert!(descriptor.split_quads(true).parse_2d().is_ok()); +} + +#[test] +fn build_incomplete() { + assert!(GridDescriptor::default() + .len_per_cell([1.0_f64, 1.0_f64, 1.0_f64]) + .parse_2d() + .is_err()); + assert!(>::default() + .n_cells([4, 4, 0]) + .parse_2d() + .is_err()); + assert!(GridDescriptor::default() + .lens([4.0_f64, 4.0_f64, 4.0_f64]) + .parse_2d() + .is_err()); +} + +#[test] +#[should_panic(expected = "length per y cell is null or negative")] +fn build_neg_lpc() { + let tmp = GridDescriptor::default() + .n_cells([4, 4, 0]) + .len_per_cell([1.0_f64, -1.0_f64, 1.0_f64]) + .parse_2d(); + let _ = tmp.unwrap(); // panic on Err(BuilderError::InvalidParameters) +} + +#[test] +#[should_panic(expected = "grid length along x is null or negative")] +fn build_null_l() { + let tmp = GridDescriptor::default() + .n_cells([4, 4, 0]) + .lens([0.0_f64, 4.0_f64, 4.0_f64]) + .parse_2d(); + let _ = tmp.unwrap(); // panic on Err(BuilderError::InvalidParameters) +} + +#[test] +#[should_panic(expected = "length per x cell is null or negative")] +fn build_neg_lpc_neg_l() { + // lpc are parsed first so their panic msg should be the one to pop + // x val is parsed first so ... + let tmp = GridDescriptor::default() + .len_per_cell([-1.0_f64, -1.0_f64, 1.0_f64]) + .lens([0.0_f64, 4.0_f64, 4.0_f64]) + .parse_2d(); + let _ = tmp.unwrap(); // panic on Err(BuilderError::InvalidParameters) +} + +// --- grid building + +#[test] +fn square_cmap2_correctness() { + let descriptor = GridDescriptor::default() + .n_cells([2, 2, 2]) + .len_per_cell([1., 1., 1.]); + let cmap: CMap2 = CMapBuilder::from(descriptor).build().unwrap(); + + // hardcoded because using a generic loop & dim would just mean + // reusing the same pattern as the one used during construction + + // face 0 + assert_eq!(cmap.face_id(1), 1); + assert_eq!(cmap.face_id(2), 1); + assert_eq!(cmap.face_id(3), 1); + assert_eq!(cmap.face_id(4), 1); + + // i-cell uses beta 0 to ensure correctness, so the iterator is BFS-like + let mut face = cmap.i_cell::<2>(1); + assert_eq!(face.next(), Some(1)); + assert_eq!(face.next(), Some(2)); // b1 + assert_eq!(face.next(), Some(4)); // b0 + assert_eq!(face.next(), Some(3)); // b1b1 + assert_eq!(face.next(), None); + + assert_eq!(cmap.beta::<1>(1), 2); + assert_eq!(cmap.beta::<1>(2), 3); + assert_eq!(cmap.beta::<1>(3), 4); + assert_eq!(cmap.beta::<1>(4), 1); + + assert_eq!(cmap.beta::<2>(1), 0); + assert_eq!(cmap.beta::<2>(2), 8); + assert_eq!(cmap.beta::<2>(3), 9); + assert_eq!(cmap.beta::<2>(4), 0); + + // face 1 + assert_eq!(cmap.face_id(5), 5); + assert_eq!(cmap.face_id(6), 5); + assert_eq!(cmap.face_id(7), 5); + assert_eq!(cmap.face_id(8), 5); + + let mut face = cmap.i_cell::<2>(5); + assert_eq!(face.next(), Some(5)); + assert_eq!(face.next(), Some(6)); + assert_eq!(face.next(), Some(8)); + assert_eq!(face.next(), Some(7)); + assert_eq!(face.next(), None); + + assert_eq!(cmap.beta::<1>(5), 6); + assert_eq!(cmap.beta::<1>(6), 7); + assert_eq!(cmap.beta::<1>(7), 8); + assert_eq!(cmap.beta::<1>(8), 5); + + assert_eq!(cmap.beta::<2>(5), 0); + assert_eq!(cmap.beta::<2>(6), 0); + assert_eq!(cmap.beta::<2>(7), 13); + assert_eq!(cmap.beta::<2>(8), 2); + + // face 2 + assert_eq!(cmap.face_id(9), 9); + assert_eq!(cmap.face_id(10), 9); + assert_eq!(cmap.face_id(11), 9); + assert_eq!(cmap.face_id(12), 9); + + let mut face = cmap.i_cell::<2>(9); + assert_eq!(face.next(), Some(9)); + assert_eq!(face.next(), Some(10)); + assert_eq!(face.next(), Some(12)); + assert_eq!(face.next(), Some(11)); + assert_eq!(face.next(), None); + + assert_eq!(cmap.beta::<1>(9), 10); + assert_eq!(cmap.beta::<1>(10), 11); + assert_eq!(cmap.beta::<1>(11), 12); + assert_eq!(cmap.beta::<1>(12), 9); + + assert_eq!(cmap.beta::<2>(9), 3); + assert_eq!(cmap.beta::<2>(10), 16); + assert_eq!(cmap.beta::<2>(11), 0); + assert_eq!(cmap.beta::<2>(12), 0); + + // face 3 + assert_eq!(cmap.face_id(13), 13); + assert_eq!(cmap.face_id(14), 13); + assert_eq!(cmap.face_id(15), 13); + assert_eq!(cmap.face_id(16), 13); + + let mut face = cmap.i_cell::<2>(13); + assert_eq!(face.next(), Some(13)); + assert_eq!(face.next(), Some(14)); + assert_eq!(face.next(), Some(16)); + assert_eq!(face.next(), Some(15)); + assert_eq!(face.next(), None); + + assert_eq!(cmap.beta::<1>(13), 14); + assert_eq!(cmap.beta::<1>(14), 15); + assert_eq!(cmap.beta::<1>(15), 16); + assert_eq!(cmap.beta::<1>(16), 13); + + assert_eq!(cmap.beta::<2>(13), 7); + assert_eq!(cmap.beta::<2>(14), 0); + assert_eq!(cmap.beta::<2>(15), 0); + assert_eq!(cmap.beta::<2>(16), 10); +} + +#[allow(clippy::too_many_lines)] +#[test] +fn splitsquare_cmap2_correctness() { + let cmap: CMap2 = CMapBuilder::unit_triangles(2).build().unwrap(); + + // hardcoded because using a generic loop & dim would just mean + // reusing the same pattern as the one used during construction + + // face 1 + assert_eq!(cmap.face_id(1), 1); + assert_eq!(cmap.face_id(2), 1); + assert_eq!(cmap.face_id(3), 1); + + let mut face = cmap.i_cell::<2>(1); + assert_eq!(face.next(), Some(1)); + assert_eq!(face.next(), Some(2)); + assert_eq!(face.next(), Some(3)); + + assert_eq!(cmap.beta::<1>(1), 2); + assert_eq!(cmap.beta::<1>(2), 3); + assert_eq!(cmap.beta::<1>(3), 1); + + assert_eq!(cmap.beta::<2>(1), 0); + assert_eq!(cmap.beta::<2>(2), 4); + assert_eq!(cmap.beta::<2>(3), 0); + + // face 4 + assert_eq!(cmap.face_id(4), 4); + assert_eq!(cmap.face_id(5), 4); + assert_eq!(cmap.face_id(6), 4); + + let mut face = cmap.i_cell::<2>(4); + assert_eq!(face.next(), Some(4)); + assert_eq!(face.next(), Some(5)); + assert_eq!(face.next(), Some(6)); + + assert_eq!(cmap.beta::<1>(4), 5); + assert_eq!(cmap.beta::<1>(5), 6); + assert_eq!(cmap.beta::<1>(6), 4); + + assert_eq!(cmap.beta::<2>(4), 2); + assert_eq!(cmap.beta::<2>(5), 9); + assert_eq!(cmap.beta::<2>(6), 13); + + // face 7 + assert_eq!(cmap.face_id(7), 7); + assert_eq!(cmap.face_id(8), 7); + assert_eq!(cmap.face_id(9), 7); + + let mut face = cmap.i_cell::<2>(7); + assert_eq!(face.next(), Some(7)); + assert_eq!(face.next(), Some(8)); + assert_eq!(face.next(), Some(9)); + + assert_eq!(cmap.beta::<1>(7), 8); + assert_eq!(cmap.beta::<1>(8), 9); + assert_eq!(cmap.beta::<1>(9), 7); + + assert_eq!(cmap.beta::<2>(7), 0); + assert_eq!(cmap.beta::<2>(8), 10); + assert_eq!(cmap.beta::<2>(9), 5); + + // face 10 + assert_eq!(cmap.face_id(10), 10); + assert_eq!(cmap.face_id(11), 10); + assert_eq!(cmap.face_id(12), 10); + + let mut face = cmap.i_cell::<2>(10); + assert_eq!(face.next(), Some(10)); + assert_eq!(face.next(), Some(11)); + assert_eq!(face.next(), Some(12)); + + assert_eq!(cmap.beta::<1>(10), 11); + assert_eq!(cmap.beta::<1>(11), 12); + assert_eq!(cmap.beta::<1>(12), 10); + + assert_eq!(cmap.beta::<2>(10), 8); + assert_eq!(cmap.beta::<2>(11), 0); + assert_eq!(cmap.beta::<2>(12), 19); + + // face 13 + assert_eq!(cmap.face_id(13), 13); + assert_eq!(cmap.face_id(14), 13); + assert_eq!(cmap.face_id(15), 13); + + let mut face = cmap.i_cell::<2>(13); + assert_eq!(face.next(), Some(13)); + assert_eq!(face.next(), Some(14)); + assert_eq!(face.next(), Some(15)); + + assert_eq!(cmap.beta::<1>(13), 14); + assert_eq!(cmap.beta::<1>(14), 15); + assert_eq!(cmap.beta::<1>(15), 13); + + assert_eq!(cmap.beta::<2>(13), 6); + assert_eq!(cmap.beta::<2>(14), 16); + assert_eq!(cmap.beta::<2>(15), 0); + + // face 16 + assert_eq!(cmap.face_id(16), 16); + assert_eq!(cmap.face_id(17), 16); + assert_eq!(cmap.face_id(18), 16); + + let mut face = cmap.i_cell::<2>(16); + assert_eq!(face.next(), Some(16)); + assert_eq!(face.next(), Some(17)); + assert_eq!(face.next(), Some(18)); + + assert_eq!(cmap.beta::<1>(16), 17); + assert_eq!(cmap.beta::<1>(17), 18); + assert_eq!(cmap.beta::<1>(18), 16); + + assert_eq!(cmap.beta::<2>(16), 14); + assert_eq!(cmap.beta::<2>(17), 21); + assert_eq!(cmap.beta::<2>(18), 0); + + // face 19 + assert_eq!(cmap.face_id(19), 19); + assert_eq!(cmap.face_id(20), 19); + assert_eq!(cmap.face_id(21), 19); + + let mut face = cmap.i_cell::<2>(19); + assert_eq!(face.next(), Some(19)); + assert_eq!(face.next(), Some(20)); + assert_eq!(face.next(), Some(21)); + + assert_eq!(cmap.beta::<1>(19), 20); + assert_eq!(cmap.beta::<1>(20), 21); + assert_eq!(cmap.beta::<1>(21), 19); + + assert_eq!(cmap.beta::<2>(19), 12); + assert_eq!(cmap.beta::<2>(20), 22); + assert_eq!(cmap.beta::<2>(21), 17); + + // face 22 + assert_eq!(cmap.face_id(22), 22); + assert_eq!(cmap.face_id(23), 22); + assert_eq!(cmap.face_id(24), 22); + + let mut face = cmap.i_cell::<2>(22); + assert_eq!(face.next(), Some(22)); + assert_eq!(face.next(), Some(23)); + assert_eq!(face.next(), Some(24)); + + assert_eq!(cmap.beta::<1>(22), 23); + assert_eq!(cmap.beta::<1>(23), 24); + assert_eq!(cmap.beta::<1>(24), 22); + + assert_eq!(cmap.beta::<2>(22), 20); + assert_eq!(cmap.beta::<2>(23), 0); + assert_eq!(cmap.beta::<2>(24), 0); +} diff --git a/honeycomb-core/src/cmap/components/betas.rs b/honeycomb-core/src/cmap/components/betas.rs index e58db941..467ffd66 100644 --- a/honeycomb-core/src/cmap/components/betas.rs +++ b/honeycomb-core/src/cmap/components/betas.rs @@ -25,6 +25,7 @@ fn new_beta_entry() -> [TVar; N] { .unwrap() } +#[allow(unused)] impl BetaFunctions { /// Constructor pub fn new(n_darts: usize) -> Self { diff --git a/honeycomb-core/src/cmap/components/unused.rs b/honeycomb-core/src/cmap/components/unused.rs index 5d85baad..b3b3ba13 100644 --- a/honeycomb-core/src/cmap/components/unused.rs +++ b/honeycomb-core/src/cmap/components/unused.rs @@ -14,6 +14,7 @@ use super::identifiers::DartIdType; /// Unused dart tracking structure. pub struct UnusedDarts(Vec>); +#[allow(unused)] impl UnusedDarts { /// Constructor pub fn new(n_darts: usize) -> Self { diff --git a/honeycomb-core/src/cmap/dim2/mod.rs b/honeycomb-core/src/cmap/dim2/mod.rs index 9e8efd7b..6d5c57a9 100644 --- a/honeycomb-core/src/cmap/dim2/mod.rs +++ b/honeycomb-core/src/cmap/dim2/mod.rs @@ -13,13 +13,11 @@ pub mod links; pub mod orbits; pub mod sews; pub mod structure; +pub mod utils; #[cfg(feature = "io")] pub mod io; -#[cfg(feature = "utils")] -pub mod utils; - // ------ CONTENT /// Number of beta functions defined for [`CMap2`]. diff --git a/honeycomb-core/src/cmap/dim2/utils.rs b/honeycomb-core/src/cmap/dim2/utils.rs index adc33354..ab2731ae 100644 --- a/honeycomb-core/src/cmap/dim2/utils.rs +++ b/honeycomb-core/src/cmap/dim2/utils.rs @@ -1,20 +1,10 @@ //! [`CMap2`] utilities implementations -//! -//!
-//! -//! **This code is only compiled if the `utils` feature is enabled.** -//! -//!
-//! -//! This module contains utility code for the [`CMap2`] structure that is gated behind the `utils` -//! feature. // ------ IMPORTS use super::CMAP2_BETA; use crate::geometry::CoordsFloat; use crate::prelude::{CMap2, DartIdType}; -use std::{fs::File, io::Write}; use stm::atomically; // ------ CONTENT @@ -25,13 +15,10 @@ impl CMap2 { /// /// # Arguments /// + /// - `const I: u8` -- Beta function to edit. /// - `dart_id: DartIdentifier` -- ID of the dart of interest. /// - `val: DartIdentifier` -- Value of the image of `dart_id` through the beta `I` function. /// - /// ## Generic - /// - /// - `const I: u8` -- Beta function to edit. - /// pub fn set_beta(&self, dart_id: DartIdType, val: DartIdType) { atomically(|trans| self.betas[(I, dart_id)].write(trans, val)); } @@ -53,230 +40,4 @@ impl CMap2 { Ok(()) }); } - - /// Computes the total allocated space dedicated to the map. - /// - /// # Arguments - /// - /// - `rootname: &str` -- root of the filename used to save results. - /// - /// # Return / Panic - /// - /// The results of this method is saved as a csv file named `_allocated.csv`. - /// The csv file is structured as follows: - /// - /// ```text - /// key, memory (bytes) - /// cat1_member1, val - /// cat1_member2, val - /// cat1_total, val - /// cat2_member1, val - /// cat2_member2, val - /// cat2_member3, val - /// cat2_total, val - /// cat3_member1, val - /// cat3_total, val - /// ``` - /// - /// It is mainly designed to be used in dedicated scripts for plotting & analysis. - /// - /// The metod may panic if, for any reason, it is unable to write to the file. - /// - /// # Example - /// - /// An example going over all three `size` methods is provided in the `honeycomb-utils` - /// crate. You can run it using the following command: - /// - /// ```shell - /// cargo run --example memory_usage - /// ``` - /// - /// The output data can be visualized using the `memory_usage.py` script. - /// - /// # Errors - /// - /// The method will return an error if: - /// - the file cannot be created, - /// - at any point, the program cannot write into the output file. - pub fn allocated_size(&self, rootname: &str) -> Result<(), std::io::Error> { - let mut file = File::create(rootname.to_owned() + "_allocated.csv")?; - writeln!(file, "key, memory (bytes)")?; - - // beta - let mut beta_total = 0; - for beta_id in 0..3 { - let mem = self.betas.capacity() * std::mem::size_of::(); - writeln!(file, "beta_{beta_id}, {mem}")?; - beta_total += mem; - } - writeln!(file, "beta_total, {beta_total}")?; - - // cells - // using 2 * sizeof(f64) bc sizeof(array) always is the size of a pointer - let geometry_vertex = self.vertices.allocated_size(); - let geometry_total = geometry_vertex; - writeln!(file, "geometry_vertex, {geometry_vertex}")?; - writeln!(file, "geometry_total, {geometry_total}")?; - - // others - let others_freedarts = self.unused_darts.len(); - let others_counters = 2 * std::mem::size_of::(); - let others_total = others_freedarts + others_counters; - writeln!(file, "others_freedarts, {others_freedarts}")?; - writeln!(file, "others_counters, {others_counters}")?; - writeln!(file, "others_total, {others_total}")?; - Ok(()) - } - - /// Computes the total used space dedicated to the map. - /// - /// # Arguments - /// - /// - `rootname: &str` -- root of the filename used to save results. - /// - /// # Return / Panic - /// - /// The results of this method is saved as a csv file named `_allocated.csv`. - /// The csv file is structured as follows: - /// - /// ```text - /// key, memory (bytes) - /// cat1_member1, val - /// cat1_member2, val - /// cat1_total, val - /// cat2_member1, val - /// cat2_member2, val - /// cat2_member3, val - /// cat2_total, val - /// cat3_member1, val - /// cat3_total, val - /// ``` - /// - /// It is mainly designed to be used in dedicated scripts for plotting & analysis. - /// - /// The metod may panic if, for any reason, it is unable to write to the file. - /// - /// # Example - /// - /// An example going over all three `size` methods is provided in the `honeycomb-utils` - /// crate. You can run it using the following command: - /// - /// ```shell - /// cargo run --example memory_usage - /// ``` - /// - /// The output data can be visualized using the `memory_usage.py` script. - /// - /// # Errors - /// - /// The method will return an error if: - /// - the file cannot be created, - /// - at any point, the program cannot write into the output file. - pub fn effective_size(&self, rootname: &str) -> Result<(), std::io::Error> { - let mut file = File::create(rootname.to_owned() + "_effective.csv")?; - writeln!(file, "key, memory (bytes)")?; - - // beta - let mut beta_total = 0; - for beta_id in 0..3 { - let mem = self.n_darts * std::mem::size_of::(); - writeln!(file, "beta_{beta_id}, {mem}")?; - beta_total += mem; - } - writeln!(file, "beta_total, {beta_total}")?; - - // cells - // using 2 * sizeof(f64) bc sizeof(array) always is the size of a pointer - let geometry_vertex = self.vertices.effective_size(); - let geometry_total = geometry_vertex; - writeln!(file, "geometry_vertex, {geometry_vertex}")?; - writeln!(file, "geometry_total, {geometry_total}")?; - - // others - let others_freedarts = self.unused_darts.len(); - let others_counters = 2 * std::mem::size_of::(); - let others_total = others_freedarts + others_counters; - writeln!(file, "others_freedarts, {others_freedarts}")?; - writeln!(file, "others_counters, {others_counters}")?; - writeln!(file, "others_total, {others_total}")?; - Ok(()) - } - - /// Computes the actual used space dedicated to the map. - /// - /// *Actual used space* refers to the total used space minus empty spots in the structure. - /// - /// # Arguments - /// - /// - `rootname: &str` -- root of the filename used to save results. - /// - /// # Return / Panic - /// - /// The results of this method is saved as a csv file named `_allocated.csv`. - /// The csv file is structured as follows: - /// - /// ```text - /// key, memory (bytes) - /// cat1_member1, val - /// cat1_member2, val - /// cat1_total, val - /// cat2_member1, val - /// cat2_member2, val - /// cat2_member3, val - /// cat2_total, val - /// cat3_member1, val - /// cat3_total, val - /// ``` - /// - /// It is mainly designed to be used in dedicated scripts for plotting & analysis. - /// - /// The metod may panic if, for any reason, it is unable to write to the file. - /// - /// # Example - /// - /// An example going over all three `size` methods is provided in the `honeycomb-utils` - /// crate. You can run it using the following command: - /// - /// ```shell - /// cargo run --example memory_usage - /// ``` - /// - /// The output data can be visualized using the `memory_usage.py` script. - /// - /// # Errors - /// - /// The method will return an error if: - /// - the file cannot be created, - /// - at any point, the program cannot write into the output file. - pub fn used_size(&self, rootname: &str) -> Result<(), std::io::Error> { - let mut file = File::create(rootname.to_owned() + "_used.csv")?; - writeln!(file, "key, memory (bytes)").unwrap(); - - let n_used_darts = self.n_darts - self.unused_darts.len(); - - // beta - let mut beta_total = 0; - for beta_id in 0..3 { - let mem = n_used_darts * std::mem::size_of::(); - writeln!(file, "beta_{beta_id}, {mem}")?; - beta_total += mem; - } - writeln!(file, "beta_total, {beta_total}")?; - - // cells - // using 2 * sizeof(f64) bc sizeof(array) always is the size of a pointer - let geometry_vertex = self.vertices.used_size(); - let geometry_total = geometry_vertex; - writeln!(file, "geometry_vertex, {geometry_vertex}")?; - writeln!(file, "geometry_total, {geometry_total}")?; - - // others - let others_freedarts = self.unused_darts.len(); - let others_counters = 2 * std::mem::size_of::(); - let others_total = others_freedarts + others_counters; - writeln!(file, "others_freedarts, {others_freedarts}")?; - writeln!(file, "others_counters, {others_counters}")?; - writeln!(file, "others_total, {others_total}")?; - Ok(()) - } } diff --git a/honeycomb-core/src/cmap/mod.rs b/honeycomb-core/src/cmap/mod.rs index e1138a8a..32489a70 100644 --- a/honeycomb-core/src/cmap/mod.rs +++ b/honeycomb-core/src/cmap/mod.rs @@ -5,7 +5,7 @@ mod components; mod dim2; mod error; -pub use builder::{BuilderError, CMapBuilder}; +pub use builder::{BuilderError, CMapBuilder, GridDescriptor}; pub use components::{ identifiers::{ DartIdType, EdgeIdType, FaceIdType, VertexIdType, VolumeIdType, NULL_DART_ID, NULL_EDGE_ID, @@ -15,6 +15,3 @@ pub use components::{ }; pub use dim2::{orbits::Orbit2, structure::CMap2}; pub use error::{CMapError, CMapResult}; - -#[cfg(feature = "utils")] -pub use builder::GridDescriptor; diff --git a/honeycomb-core/src/lib.rs b/honeycomb-core/src/lib.rs index ffd3bb5e..fc32e2da 100644 --- a/honeycomb-core/src/lib.rs +++ b/honeycomb-core/src/lib.rs @@ -21,7 +21,7 @@ // --- enable doc_auto_cfg feature if compiling in nightly #![allow(unexpected_cfgs)] #![cfg_attr(nightly, feature(doc_auto_cfg))] -// --- some though love for the code +// --- some tough love for the code #![warn(missing_docs)] #![warn(clippy::pedantic)] // --- some tolerance diff --git a/honeycomb-core/src/prelude.rs b/honeycomb-core/src/prelude.rs index 92d470bf..b0c45d89 100644 --- a/honeycomb-core/src/prelude.rs +++ b/honeycomb-core/src/prelude.rs @@ -2,13 +2,8 @@ pub use crate::attributes::{AttributeBind, AttributeUpdate}; pub use crate::cmap::{ - BuilderError, CMap2, CMapBuilder, CMapResult, DartIdType, EdgeIdType, FaceIdType, Orbit2, - OrbitPolicy, VertexIdType, VolumeIdType, NULL_DART_ID, NULL_EDGE_ID, NULL_FACE_ID, - NULL_VERTEX_ID, NULL_VOLUME_ID, + BuilderError, CMap2, CMapBuilder, CMapResult, DartIdType, EdgeIdType, FaceIdType, + GridDescriptor, Orbit2, OrbitPolicy, VertexIdType, VolumeIdType, NULL_DART_ID, NULL_EDGE_ID, + NULL_FACE_ID, NULL_VERTEX_ID, NULL_VOLUME_ID, }; pub use crate::geometry::{CoordsError, CoordsFloat, Vector2, Vertex2}; - -// ------ FEATURE-GATED RE-EXPORTS - -#[cfg(feature = "utils")] -pub use crate::cmap::GridDescriptor; diff --git a/honeycomb-kernels/Cargo.toml b/honeycomb-kernels/Cargo.toml index df0075d7..2b7c9416 100644 --- a/honeycomb-kernels/Cargo.toml +++ b/honeycomb-kernels/Cargo.toml @@ -13,7 +13,7 @@ authors.workspace = true publish = true [dependencies] -honeycomb-core = { workspace = true, features = ["io", "utils"] } +honeycomb-core = { workspace = true, features = ["io"] } num-traits.workspace = true rayon = "1.10.0" thiserror.workspace = true diff --git a/honeycomb/Cargo.toml b/honeycomb/Cargo.toml index 7f11138f..a4655b30 100644 --- a/honeycomb/Cargo.toml +++ b/honeycomb/Cargo.toml @@ -18,6 +18,6 @@ kernels = ["dep:honeycomb-kernels"] render = ["dep:honeycomb-render"] [dependencies] -honeycomb-core = { workspace = true, features = ["io", "utils"] } +honeycomb-core = { workspace = true, features = ["io"] } honeycomb-kernels = { workspace = true, optional = true } honeycomb-render = { workspace = true, optional = true }