Source code for Muscat.Containers.ElementsContainers

# -*- coding: utf-8 -*-
#
# This file is subject to the terms and conditions defined in
# file 'LICENSE.txt', which is part of this source code package.
#
from __future__ import annotations
from typing import Union, List, Any, Tuple, Iterator, NoReturn, Union, Iterable, Generator, Optional
import bisect

import numpy as np

from Muscat.Helpers.Decorators import froze_it
from Muscat.Types import MuscatIndex, MuscatFloat, ArrayLike, NDArray
import Muscat.Containers.ElementsDescription as ED
from Muscat.Containers.Tags import Tags, Tag
from Muscat.Helpers.Interpolation import BinarySearch
from Muscat.FE.Spaces.FESpaces import LagrangeSpaceP1


[docs]@froze_it class ElementsContainer: """ Class to hold a list of elements of the same type * elementType : a string from Muscat.Containers.ElementsDescription * connectivity : the connectivity matrix starting form 0 * tags : an instance of the Tags class * originalIds : the id or number from the previous mesh/file The user can use this data to find the mapping from the initial mesh/file to the current mesh (self). * self.cpt : an internal counter to do efficient add of elements one by one The user is responsible to call self.Tighten() to compact the connectivity matrix after the population ( calls AddNewElement(...) or allocate(...)) """ def __init__(self, elementType: Union[ED.ElementType, str]): super().__init__() self.elementType: ED.ElementType = ED.ElementType(elementType) self.connectivity: NDArray[MuscatIndex] = np.empty((0, ED.numberOfNodes[elementType]), dtype=MuscatIndex) self.tags: Tags = Tags() self.cpt: MuscatIndex = 0 self.originalIds: np.ndarray = np.empty((0,), dtype=MuscatIndex) def __eq__(self, other: Any) -> bool: """return True if 2 container are equal Parameters ---------- other : ElementsContainer _description_ Returns ------- bool True if the 2 containers are equal, False ir not """ if not isinstance(other, ElementsContainer): return False if self.elementType != other.elementType: return False if not np.array_equal( self.connectivity[ 0 : self.cpt, :, ], other.connectivity[0 : other.cpt, :], ): return False if not np.array_equal(self.originalIds[: self.cpt], other.originalIds[: other.cpt]): return False if self.tags != other.tags: return False return True
[docs] def View(self) -> ElementsContainer: """return a copy of this object recursively, numpy arrays are view to the original ones but with the flags.writeable = False Returns ------- ElementsContainer a view of ElementsContainer """ res = ElementsContainer(self.elementType) res.connectivity = self.connectivity res.connectivity.flags.writeable = False res.tags = self.tags.View() res.cpt = self.cpt res.originalIds = self.originalIds.view() res.originalIds.flags.writeable = False return res
[docs] def GetNumberOfElements(self) -> MuscatIndex: """return the number of elements in this container Returns ------- int Number of elements """ return self.cpt
# return self.connectivity.shape[0]
[docs] def Merge(self, other: ElementContainerLike, offset: Optional[MuscatIndex] = None) -> None: """ Merge the elements from the other container into this. Warning!!! Non elimination of double elements is done if an offset is supplied the connectivity of the other container is shifted by the value of the offset during the merge. Useful for after merging the nodes from the other mesh. the originalIds from other are multiplied by -1 before merging it to self.originalIds """ if self is other: raise RuntimeError("self and other are the same object") if other.cpt == 0: return self.Reserve(self.cpt + other.cpt) if offset is None: offset = 0 self.connectivity[self.cpt :, :] = other.connectivity[0 : other.cpt, :] + offset self.originalIds[self.cpt :] = -1 * np.arange(other.cpt) for tag in other.tags: self.GetTag(tag.name).AddToTag(tag.GetIds() + self.cpt) self.cpt += other.cpt
[docs] def AddNewElements(self, connectivity: ArrayLike, originalIds: Optional[ArrayLike] = None) -> int: """Append a new elements to the connectivity Parameters ---------- conn : ArrayLike connectivity of the added elements originalIds : ArrayLike, optional the original id of the added element, by default -1 is used Returns ------- int the total number of elements in the container """ connectivity = np.asarray(connectivity) originalNOE = self.GetNumberOfElements() self.Allocate(originalNOE + connectivity.shape[0]) self.connectivity[originalNOE : originalNOE + connectivity.shape[0], :] = connectivity if originalIds is None: self.originalIds[originalNOE : originalNOE + connectivity.shape[0]] = -1 else: originalIds = np.asarray(originalIds) self.originalIds[originalNOE : originalNOE + connectivity.shape[0]] = originalIds return self.cpt
[docs] def AddNewElement(self, connectivity: ArrayLike, originalId: int) -> int: """append a single element to the connectivity Parameters ---------- connectivity : ArrayLike connectivity of the added element originalId : int the original id of the added element Returns ------- int the total number of elements in the container """ if self.cpt >= self.connectivity.shape[0]: self.Reserve(2 * self.cpt + 1) self.connectivity[self.cpt, :] = connectivity self.originalIds[self.cpt] = originalId self.cpt += 1 return self.cpt
[docs] def GetNumberOfNodesPerElement(self) -> int: """return the number of nodes per element for the elements in this container Returns ------- int the number of nodes per element for the elements in this container """ return ED.numberOfNodes[self.elementType]
[docs] def GetNodesIndexFor(self, ids: Union[int, ArrayLike]) -> np.ndarray: """return the nodes used by the list of elements input: ids : Parameters ---------- ids : Union[int,ArrayLike] the id or a list of ids of elements to treat (always a local id list) Returns ------- np.ndarray the ids of the nodes used by the elements ids """ return np.unique(self.connectivity[ids, :])
[docs] def GetTag(self, tagName: str) -> Tag: """return the tag by name. If the tag does not exist a new tag is created Parameters ---------- tagName : str the name of the tag to retrieve Returns ------- Tag a tag with the name tagName """ return self.tags.CreateTag(tagName, False)
[docs] def Reserve(self, nbElements: Union[int, MuscatIndex]) -> None: """Reserve the storage for nbElements The user is responsible to call self.Tighten() to compact the connectivity and the originalIds. matrix after the population Parameters ---------- nbElements : int Number of element to reserve """ if nbElements != self.connectivity.shape[0]: self.connectivity = np.resize(self.connectivity, (nbElements, self.GetNumberOfNodesPerElement())) self.originalIds = np.resize(self.originalIds, (nbElements,))
[docs] def Allocate(self, nbElements: Union[int, MuscatIndex]) -> None: """Allocate the storage for nbElements the user is responsible of filling the connectivity and the originalIds with valid values Parameters ---------- nbElements : int, MuscatIndex Number of element to Allocate """ self.Reserve(nbElements) self.cpt = nbElements
[docs] def Tighten(self) -> None: """Compact the storage and free non used space.""" self.Reserve(self.cpt) self.tags.Tighten()
def __str__(self) -> str: """Return a string representation""" res = " ElementsContainer, " res += f" Type : ({self.elementType},{self.GetNumberOfElements()}), " res += " Tags : " + " ".join([("(" + x.name + ":" + str(len(x)) + ")") for x in self.tags]) + "\n" return res
[docs] def ConvertDataForNativeTreatment(self) -> None: self.connectivity = np.asarray(self.connectivity, dtype=MuscatIndex, order="C")
[docs] def AddElementToTag(self, tagname: str, ids: Union[int, List[int]]) -> None: self.tags.CreateTag(tagname, False).AddToTag(ids)
[docs]@froze_it class StructuredElementsContainer: """Element container of a topologically regular array of elements. This class can hold regular element in 2D and 3D. Pixels and Voxel. The grid is defined by the dimensions (number of nodes in each direction) """ def __init__(self, elementType: Union[ED.ElementType, str]) -> None: """init this class Parameters ---------- elementType : str The type of element ot create the rectilinear can only be Hexahedron_8 or Quadrangle_4 """ super().__init__() if elementType not in [ED.Hexahedron_8, ED.Quadrangle_4]: raise RuntimeError("Only elements of type ED.Hexahedron_8, ED.Quadrangle_4 are supported on StructuredElementContainer ") self.elementType = elementType self.space = LagrangeSpaceP1[elementType] self.space.Create() self.tags = Tags() self._connectivity = None self.originalIds = np.empty((0,), dtype=MuscatIndex) # internal data self.nodesPerElement = 2 ** self.GetDimensionality() self.__dimensions = None
[docs] def View(self) -> StructuredElementsContainer: """return a copy of this object recursively, numpy arrays are view to the original ones but with the flags.writeable = False Returns ------- StructuredElementsContainer a view of StructuredElementsContainer """ res = StructuredElementsContainer(self.elementType) res.SetDimensions(self.__dimensions) if self._connectivity is not None: res._connectivity = self._connectivity.view() res._connectivity.flags.writeable = False res.originalIds = self.originalIds.view() res.originalIds.flags.writeable = False return res
def __eq__(self, other: Any) -> bool: """return True if 2 container are equal Parameters ---------- other : Any _description_ Returns ------- bool True if the 2 containers are equal, False ir not """ if not isinstance(other, StructuredElementsContainer): return False if self.elementType != other.elementType: return False if not np.array_equal(self.__dimensions, other.__dimensions): return False if not np.array_equal(self.originalIds, other.originalIds): return False if self.tags != other.tags: return False return True @property def connectivity(self) -> np.ndarray: """Generate and retrieve the connectivity of the elements Returns ------- np.ndarray Connectivity of the elements size (self.GetNumberOfElements(), 4 or 8) """ if self._connectivity is None: self._connectivity = self.GetConnectivityForElements(np.arange(self.GetNumberOfElements())) self._connectivity.flags.writeable = False return self._connectivity @property def cpt(self): return self.GetNumberOfElements()
[docs] def SetDimensions(self, data: ArrayLike) -> None: """Set the number of points for the grid in each dimension Parameters ---------- data : ArrayLike the number of points in each dimension for this grid """ data = np.asarray(data, dtype=MuscatIndex) if len(data) != ED.ElementsInfo[self.elementType].dimension: raise RuntimeError(f"Data must be compatible with the dimensionality of {self.elementType}") self.__dimensions = np.array(data, dtype=MuscatIndex) self.originalIds = np.arange(self.GetNumberOfElements(), dtype=MuscatIndex)
[docs] def GetDimensionality(self) -> int: """Return the dimensionality (2 for 2D, 3 for 3D) of this container Returns ------- int the dimensionality of this containers """ return ED.ElementsInfo[self.elementType].dimension
[docs] def GetConnectivityForElements(self, indices: ArrayLike) -> np.ndarray: """Return the connectivity for the element listed in the indices array. If the connectivity is used many times, a simple call of self.connectivity will generate the connectivity for all the elements and keep it for later use. Parameters ---------- indices : ArrayLike the indices of the elements to generate the connectivity Returns ------- np.ndarray the connectivity of the selected elements size = (len(indices), 4 or 8 ) """ if not isinstance(indices, (np.ndarray, list)): return self.GetMultiIndexOfElements([indices]) elements_ijk = self.GetMultiIndexOfElements(np.asarray(indices, dtype=MuscatIndex)) if self.GetDimensionality() == 3: res = np.empty((elements_ijk.shape[0], 8), dtype=MuscatIndex) # n0 res[:, 0] = elements_ijk[:, 0] * self.__dimensions[1] * self.__dimensions[2] + elements_ijk[:, 1] * self.__dimensions[2] + elements_ijk[:, 2] # n1 res[:, 1] = res[:, 0] + self.__dimensions[1] * self.__dimensions[2] res[:, 2] = res[:, 1] + self.__dimensions[2] res[:, 3] = res[:, 0] + self.__dimensions[2] res[:, 4:8] = res[:, 0:4] + 1 return res else: res = np.empty((elements_ijk.shape[0], 4), dtype=MuscatIndex) res[:, 0] = elements_ijk[:, 0] * self.__dimensions[1] + elements_ijk[:, 1] res[:, 1] = res[:, 0] + self.__dimensions[1] res[:, 2] = res[:, 1] + 1 res[:, 3] = res[:, 0] + 1 return res
[docs] def GetMultiIndexOfElements(self, indices: Union[ArrayLike, int]) -> np.ndarray: """Return the multi-index ijk for element with indices Parameters ---------- indices : ArrayLike the indices of the elements to treat Returns ------- np.ndarray an array with the ijk indices for every element in indices size (nb element, 2 (ij) or 3 (ijk) ) """ if not isinstance(indices, (np.ndarray, list)): return self.GetMultiIndexOfElements([indices]) indices = np.asarray(indices, dtype=MuscatIndex) if self.GetDimensionality() == 3: planeSize = (self.__dimensions[1] - 1) * (self.__dimensions[2] - 1) res = np.empty((len(indices), 3), dtype=MuscatIndex) res[:, 0] = indices // planeSize resYZ = indices - res[:, 0] * planeSize res[:, 1] = resYZ // (self.__dimensions[2] - 1) res[:, 2] = resYZ - res[:, 1] * (self.__dimensions[2] - 1) return res else: res = np.empty((len(indices), 2), dtype=MuscatIndex) planeSize = self.__dimensions[1] - 1 res[:, 0] = indices // planeSize res[:, 1] = indices - res[:, 0] * planeSize return res
[docs] def GetMultiIndexOfElement(self, index: int) -> np.ndarray: """Return the multi-index ijk for an element please see documentation for GetMultiIndexOfElements Parameters ---------- index : int the index of the element Returns ------- np.ndarray ij or ijk for the element """ return self.GetMultiIndexOfElements([index])[0, :]
[docs] def GetNumberOfElements(self) -> MuscatIndex: """Return the number of element in this container Returns ------- int the number of elements """ return np.prod((self.__dimensions - 1)[self.__dimensions >= 1])
[docs] def Merge(self, other: ElementsContainer, offset: int = None) -> NoReturn: """Not possible for a StructuredElementContainer""" raise RuntimeError("Merge is not possible for a StructuredElementContainer")
[docs] def AddNewElements(self, connectivity: ArrayLike, originalIds: ArrayLike = None) -> NoReturn: """Not possible for a StructuredElementContainer""" raise RuntimeError("AddNewElements is not possible for a StructuredElementContainer")
[docs] def AddNewElement(self, connectivity: ArrayLike, originalId: int) -> NoReturn: """Not possible for a StructuredElementContainer""" raise RuntimeError("AddNewElement is not possible for a StructuredElementContainer")
[docs] def GetNumberOfNodesPerElement(self) -> MuscatIndex: """Return the number of node per element Returns ------- int number of nodes """ return MuscatIndex(2) ** len(self.__dimensions)
[docs] def GetNodesIndexFor(self, ids: Union[int, ArrayLike]) -> np.ndarray: """return the nodes used by the list of elements input: ids : Parameters ---------- ids : Union[int,ArrayLike] the id or a list of ids of elements to treat (always a local id list) Returns ------- np.ndarray the ids of the nodes used by the elements ids """ return np.unique(self.GetConnectivityForElements(ids))
[docs] def GetTag(self, tagName: str) -> Tag: """Return the Tag by the name if the tag does not exist a new tag is created Parameters ---------- tagName : str The name of the tag Returns ------- Tag an instance of type Tag """ return self.tags.CreateTag(tagName, False)
[docs] def AddElementToTag(self, tagname: str, ids: Union[int, List[int]]): self.tags.CreateTag(tagname, False).AddToTag(ids)
def __str__(self) -> str: """return a string of a summary of this container Returns ------- str The description """ res = f" StructuredElementContainer({self.__dimensions}), " res += f" Type : ({self.elementType},{self.GetNumberOfElements()}), " res += " Tags : " + " ".join([("(" + x.name + ":" + str(len(x)) + ")") for x in self.tags]) + "\n" return res
[docs] def Tighten(self) -> None: """Tighten all the tags (free unused memory) Call Tag.Tighten on every tag """ self.tags.Tighten()
[docs] def ConvertDataForNativeTreatment(self) -> None: pass
ElementContainerLike = Union[ElementsContainer, StructuredElementsContainer] """Meta type for the element type names"""
[docs]@froze_it class AllElements: """ Class to store a list of element containers. This class is mostly a re-implementation of dict but with ordered elementType. This class is sorted by elementTypes in lexicographic order, so the retrieving order is stable, and independent on the order of filling. """ def __init__(self) -> None: super().__init__() self.storage: List[ElementContainerLike] = [] def __eq__(self, other: Any) -> bool: """return True if 2 AllElements are equal Parameters ---------- other : AllElements the other instance Returns ------- bool True if the 2 instances are equal, False ir not """ if len(self.storage) != len(other.storage): return False # because the storage are sorted we can do: for l, r in zip(self.storage, other.storage): if l != r: return False return True
[docs] def View(self) -> AllElements: """return a copy of this object recursively, numpy arrays are view to the original ones but with the flags.writeable = False Returns ------- AllElements a view of AllElements """ res = AllElements() for ec in self: res.AddContainer(ec.View()) return res
[docs] def MergeElements(self, other: AllElements, nodesOffset: int = 0) -> None: """Merge the elements for a AllElements into this (self) Parameters ---------- other : AllElements the element to merge into this nodesOffset : int, optional an node offset to use to shift the connectivity of the elements, by default 0 """ for elementType, data in other.items(): self.GetElementsOfType(elementType).Merge(data, offset=nodesOffset)
[docs] def keys(self) -> Iterable[ED.ElementType]: """Return the list of elementType present on the mesh Returns ------- List list of elementType in the mesh """ return [ec.elementType for ec in self.storage]
# return (ec.elementType for ec in self.storage) # return map(lambda x: x.elementType, self.storage)
[docs] def values(self) -> List: """return the internal storage. .. note:: This list must be kept in alphabetical order for consistency Returns ------- List list of ElementsContainer """ return self.storage
def __iter__(self) -> Iterator: """Iterator on the internal storage Returns ------- Iterator Iterator of ElementsContainer """ return iter(self.storage)
[docs] def items(self) -> Generator[Tuple[ED.ElementType, ElementContainerLike], None, None]: """List of (elementType, ElementsContainer) Returns ------- List[Tuple] _description_ """ return ((x.elementType, x) for x in self.storage)
def __delitem__(self, key: Union[ED.ElementType, str]) -> None: """Remove an element container using elementType Parameters ---------- key : str the elementType to be eliminated """ index = [x.elementType for x in self.storage].index(key) del self.storage[index] def __getitem__(self, key: Union[ED.ElementType, str]) -> ElementsContainer: """getitem operator, no creation of elementsContainer is done Parameters ---------- key : str elementType to be extracted Returns ------- ElementContainer the extracted instance """ index = self.Index(key) return self.storage[index]
[docs] def AddContainer(self, value: Union[ED.ElementsContainer, StructuredElementsContainer]) -> None: """add an ElementsContainer Parameters ---------- value : ElementsContainer the ElementsContainer to be added """ key = value.elementType keys = self.keys() if key in keys: index = self.Index(key) self.storage[index] = value else: index = bisect.bisect_right(keys, key) self.storage.insert(index, value)
[docs] def Index(self, elementType: Union[ED.ElementType, str]) -> int: """Get the position of the container of type elementType inside the storage""" keys = self.keys() return keys.index(elementType)
def __len__(self) -> int: """Return the number of ElementsContainer in the storage Returns ------- int number of ElementsContainer """ return len(self.storage) def __contains__(self, elementType: Union[ED.ElementType, str]) -> bool: """True if elementType exist in the storage Parameters ---------- elementType : str elementType name """ return elementType in self.keys()
[docs] def GetElementsOfType(self, typename: Union[ED.ElementType, str]) -> ElementsContainer: """return an ElementContainer by name, if the container does not exist a new is inserted and returned Parameters ---------- typename : str the name of the elements to retrieve Returns ------- ElementsContainer the container """ if typename not in self: self.AddContainer(ElementsContainer(typename)) return self[typename]
[docs] def GetTagsNames(self) -> List[str]: """return a list with all the tags names in this container Returns ------- List[str] the list of the tag names """ res = set() for data in self.values(): res.update(data.tags.keys()) return list(res)
def __str__(self) -> str: """String representation Returns ------- str description """ res = "" for data in self.storage: res += str(data) return res
[docs]def CheckIntegrity(GUI: bool = False): from Muscat.Helpers.CheckTools import MustFailFunction ec_T3 = ElementsContainer(ED.Triangle_3) ec_T3.ConvertDataForNativeTreatment() ec_T3.Allocate(1) ec_T3.connectivity[0, :] = [0, 1, 2] ec_T3.originalIds[0] = 0 ec_T3.tags.CreateTag("First").SetIds([0]) ec_T3.AddElementToTag("First2", [0]) ec_T3.View() assert ec_T3 != object # different types ec_T6 = ElementsContainer(ED.Triangle_6) assert ec_T3 != ec_T6 # different elements types ec_T3_II = ElementsContainer(ED.Triangle_3) ec_T3_II.Allocate(1) ec_T3_II.connectivity[0, :] = [0, 1, 20] ec_T3_II.originalIds[0] = 1 assert ec_T3 != ec_T3_II # diff connectivity ec_T3_II.connectivity[0, :] = [0, 1, 2] assert ec_T3 != ec_T3_II # diff originalIds ec_T3_II.originalIds[0] = 0 assert ec_T3 != ec_T3_II # diff tags ec_T3_II.tags.CreateTag("First").SetIds([0]) ec_T3_II.AddElementToTag("First2", [0]) assert ec_T3 == ec_T3_II # equal meshes ec_T3.Merge(ElementsContainer(ED.Triangle_3)) ec_T3.Merge(ec_T3_II, 3) ec_T3.Merge(ec_T3_II) ec_T3.AddNewElements([[4, 5, 6]]) ec_T3.AddNewElements([[7, 8, 9]], [0]) ec_T3.AddNewElement([10, 11, 12], 2) assert ec_T3.GetNumberOfNodesPerElement() == 3 AssertionError(ec_T3.GetNumberOfElements() == 2) np.testing.assert_equal(ec_T3.GetNodesIndexFor([0, 1]), [0, 1, 2, 3, 4, 5]) ec_T3.Tighten() print(ec_T3) MustFailFunction(ec_T3.Merge, ec_T3) ### StructuredElementContainer ## MustFailFunction(StructuredElementsContainer, ED.Bar_2) ### StructuredElementContainer 2D ## ec2 = StructuredElementsContainer(ED.Quadrangle_4) ec2.ConvertDataForNativeTreatment() ec2.SetDimensions([2, 2]) ec2.AddElementToTag("First", [0]) np.testing.assert_equal(ec2.GetMultiIndexOfElements(0), [[0, 0]]) MustFailFunction(ec2.SetDimensions, [3, 3, 3]) coon = ec2.connectivity ec2.View() assert ec2.GetNumberOfNodesPerElement() == 4 ### StructuredElementContainer 2D ## ec = StructuredElementsContainer(ED.Hexahedron_8) ec.SetDimensions([2, 2, 2]) coon = ec.connectivity print(ec.GetConnectivityForElements(0)) np.testing.assert_equal(ec.GetMultiIndexOfElement(0), [0, 0, 0]) assert ec.GetNumberOfNodesPerElement() == 8 ec.GetTag("testTag") print(ec) ec.Tighten() assert ec != 0 assert ec != ec2 other = StructuredElementsContainer(ED.Hexahedron_8) assert ec != other other.SetDimensions([2, 2, 2]) assert ec != other other.originalIds += 1 assert ec != other assert ec == ec np.testing.assert_equal(ec.GetNodesIndexFor([0]), [0, 1, 2, 3, 4, 5, 6, 7]) MustFailFunction(ec.Merge, other) MustFailFunction(ec.AddNewElements, [[]]) MustFailFunction(ec.AddNewElement, [], 0) ### AllElements ## # check order of insertion and preservation of the order of extractions aeI = AllElements() aeI.AddContainer(ec_T3) aeI.AddContainer(ec2) print(aeI) aeII = AllElements() aeII.AddContainer(ec2) aeII.AddContainer(ec_T3) np.testing.assert_equal(aeI.keys(), aeII.keys()) ae = AllElements() ae.AddContainer(ec_T3) ae.AddContainer(ec_T3) ae__II = AllElements() assert ae__II != ae ae__II.AddContainer(ec_T6) assert ae__II != ae assert ae != ae__II del ae__II[ec_T6.elementType] assert ae__II != ae ae__II.AddContainer(ec_T3_II) assert ae != ae__II del ae__II[ec_T3_II.elementType] ae__II.AddContainer(ec_T3) assert ae == ae__II assert ae__II == ae ae__II.View() np.testing.assert_equal(ae.values(), [ec_T3]) for ec in ae: assert ec is ec_T3 for elementType, data in ae.items(): assert elementType == ED.Triangle_3 assert data == ec_T3 assert len(ae) == 1 assert ae.GetElementsOfType(ED.Triangle_3) == ec_T3 ae.GetElementsOfType(ED.Triangle_6) print(ae.GetTagsNames()) print(ae) AllElements().MergeElements(ae) return "ok"
if __name__ == "__main__": print(CheckIntegrity(True)) # pragma: no cover