Source code for mindelec.geometry.geometry_base

# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""base classes for geometry"""

from __future__ import absolute_import
from abc import abstractmethod
import copy
import numpy as np

GEOM_TYPES = ["domain", "BC", "IC", "time"]
DATA_TYPES = (np.int32, np.int64, np.float16, np.float32, np.float64)


[docs]class PartSamplingConfig: r""" Definition of partial sampling configuration. Args: size (Union[int, tuple[int], list[int]): number of sampling points. random_sampling (bool): Specifies whether randomly sampling points. Default: True. sampler (str): method for random sampling. Default: uniform. random_merge (bool): Specifies whether randomly merge coordinates of different dimensions. Default: True. with_normal (bool): Specifies whether generating the normal vectors of the boundary. Default: False. Raises: TypeError: size is not int number when random sampling. Supported Platforms: ``Ascend`` Examples: >>> from mindelec.geometry import PartSamplingConfig >>> partsampling = PartSamplingConfig(100, True, "uniform", True, True) """ def __init__(self, size, random_sampling=True, sampler="uniform", random_merge=True, with_normal=False): if isinstance(size, bool) or not isinstance(size, (tuple, list, int)): raise TypeError("The type of size should be int, tuple or list but got {}".format(type(size))) if isinstance(size, (tuple, list)): for ele in size: _check_param_type(ele, "element in size", int) _check_param_type(random_sampling, "random_sampling", bool) _check_param_type(sampler, "sampler", str) _check_param_type(random_merge, "random_merge", bool) _check_param_type(with_normal, "with_normal", bool) self.random_sampling = random_sampling self.sampler = sampler self.size = size self.random_merge = random_merge self.with_normal = with_normal
[docs]class SamplingConfig: r""" Definition of global sampling configuration. Args: part_sampling_dict (dict): sampling configuration. Raises: ValueError: If `coord_min` or `coord_max` is neither int nor float . TypeError: If `part_sampling_dict` is not dict. KeyError: If `geom_type` not "domain", "BC", "IC" or "time". TypeError: If 'config' is not PartSamplingConfig object. ValueError: If `self.domain.size` is neither list nor tuple. ValueError: If `self.ic.size` is neither list nor tuple. ValueError: If `self.time.size` is neither list nor tuple. Supported Platforms: ``Ascend`` Examples: >>> from mindelec.geometry import SamplingConfig, PartSamplingConfig >>> part_sampling_config_dict = {"domain" : PartSamplingConfig([100, 100], False, True), ... "BC" : PartSamplingConfig(100, True, "uniform", True, True)} >>> sampling_config = SamplingConfig(part_sampling_config_dict) """ def __init__(self, part_sampling_dict): _check_dict(part_sampling_dict) _check_geom_type(part_sampling_dict) self.domain = None if "domain" not in part_sampling_dict.keys() else part_sampling_dict["domain"] _check_size_type(self.domain) self.bc = None if "BC" not in part_sampling_dict.keys() else part_sampling_dict["BC"] if self.bc is not None and not self.bc.random_sampling: if isinstance(self.bc.size, (list, tuple)): size = np.array(self.bc.size).astype(np.int64) self.bc.size = np.prod(size) self.bc.size = np.array(self.bc.size).astype(np.int64) self.ic = None if "IC" not in part_sampling_dict.keys() else part_sampling_dict["IC"] _check_size_type(self.ic) self.time = None if "time" not in part_sampling_dict.keys() else part_sampling_dict["time"] _check_size_type(self.time)
[docs]class Geometry: r""" Definition of Geometry object. Args: name (str): name of the geometry. dim (int): number of dimensions. coord_min (Union[int, float, list[int, float], tuple[int, float], numpy.ndarray]): minimal coordinate of the geometry. coord_max (Union[int, float, list[int, float], tuple[int, float], numpy.ndarray]): maximal coordinate of the geometry. dtype (numpy.dtype): Data type of sampled point data type. Default: numpy.float32. sampling_config (SamplingConfig): sampling configuration. Default: None Supported Platforms: ``Ascend`` Examples: >>> from easydict import EasyDict as edict >>> from mindelec.geometry import create_config_from_edict, Geometry >>> geometry_config = edict({'domain' : edict({'random_sampling' : True, 'size' : 100}), ... 'BC' : edict({'random_sampling' : True, 'size' : 100, 'sampler' : 'uniform',}), ... 'random_merge' : True,}) >>> sampling_config = create_config_from_edict(geometry_config) >>> geom = Geometry("geom", 1, 0.0, 1.0, sampling_config=sampling_config) >>> geom.set_name("geom_name") """ def __init__(self, name, dim, coord_min, coord_max, dtype=np.float32, sampling_config=None): if not isinstance(name, str): raise TypeError("geometry name must be string, but got: {}, type: {}".format(name, type(name))) self.name = name if not isinstance(dim, int) or isinstance(dim, bool): raise TypeError("dim type should be integer, but got dim: {}, type: {}".format(dim, type(dim))) self.dim = dim if self.dim <= 0: raise ValueError("dimension should not be non-positive, but got dim: {}".format(self.dim)) supported_type = (int, float, np.ndarray, list, tuple) if isinstance(coord_min, bool) or isinstance(coord_max, bool) or \ not isinstance(coord_min, supported_type) or not isinstance(coord_max, supported_type): raise TypeError("argument coord_min/max must be {}, but got coord_min: {} with type: {};" "coord_max: {} with type: {}".format(supported_type, coord_min, type(coord_min), coord_max, type(coord_max))) if isinstance(coord_min, (int, float)): coord_min = [coord_min] if isinstance(coord_max, (int, float)): coord_max = [coord_max] self.coord_min, self.coord_max = np.array(coord_min), np.array(coord_max) for ele in np.concatenate((self.coord_min, self.coord_max), axis=0): if not isinstance(ele, DATA_TYPES): raise TypeError("element for coord should be int or float, but got ele: {}, type: {}".format( ele, type(ele))) if len(coord_min) != self.dim or len(coord_max) != self.dim: raise ValueError("dimension of coordinates array must be equal with dim, but got dim: {}," "coord_min: {} with length {}, coord_max {} with length {}".format( dim, coord_min, len(coord_min), coord_max, len(coord_max))) if np.any(self.coord_max - self.coord_min <= 0.0): raise ValueError("coord_min should be smaller than coord_max, but got coord_min: {}, coord_max: {}".format( self.coord_min, self.coord_max)) if dtype not in DATA_TYPES: raise TypeError("Unsupported datatype: {}, only: {} are supported".format(dtype, DATA_TYPES)) self.dtype = dtype if sampling_config is not None and not isinstance(sampling_config, SamplingConfig): raise TypeError("sampling_config should be instance of SamplingConfig, bug got {} with type {}".format( sampling_config, type(sampling_config) )) self.sampling_config = sampling_config self.geom_type = type(self).__name__
[docs] def set_name(self, name): """ set geometry instance name Args: name (str): name of geometry instance Raises: TypeError: If `name` is not string. Examples: >>> from mindelec.geometry import create_config_from_edict, Geometry >>> geom = Geometry("geom", 1, 0.0, 1.0) >>> geom.set_name("geom_name") """ if not isinstance(name, str): raise TypeError("geometry name must be string, but got: {}, type: {}".format(name, type(name))) self.name = name
[docs] def set_sampling_config(self, sampling_config: SamplingConfig): """ set sampling info Args: sampling_config (SamplingConfig): sampling configuration. Raises: TypeError: If `sampling_config` is not instance of SamplingConfig. Examples: >>> from easydict import EasyDict as edict >>> from mindelec.geometry import create_config_from_edict, Geometry >>> geometry_config = edict({'domain': edict({'random_sampling': True, 'size': 100}), ... 'BC': edict({'random_sampling': True, 'size': 100, 'sampler': 'uniform',}), ... 'random_merge': True,}) >>> sampling_config = create_config_from_edict(geometry_config) >>> geom = Geometry("geom", 1, 0.0, 1.0) >>> geom.set_sampling_config(sampling_config) """ if not isinstance(sampling_config, SamplingConfig): raise TypeError("sampling_config: {} should be instance of class SamplingConfig, bug got: {}".format( sampling_config, type(sampling_config))) self.sampling_config = copy.deepcopy(sampling_config)
@abstractmethod def _inside(self, points, strict=False): raise NotImplementedError("{}._inside not implemented".format(self.geom_type)) @abstractmethod def _on_boundary(self, points): raise NotImplementedError("{}._on_boundary not implemented".format(self.geom_type)) @abstractmethod def _boundary_normal(self, points): raise NotImplementedError("{}._boundary_normal not implemented".format(self.geom_type)) @abstractmethod def sampling(self, geom_type="domain"): raise NotImplementedError("{}.sampling not implemented".format(self.geom_type)) def __and__(self, geom): return self.intersection(geom) def intersection(self, geom, sampling_config=None): from .csg import CSGIntersection return CSGIntersection(self, geom, sampling_config) def __or__(self, geom): return self.union(geom) def union(self, geom, sampling_config=None): from .csg import CSGUnion return CSGUnion(self, geom, sampling_config) def __sub__(self, geom): return self.difference(geom) def difference(self, geom, sampling_config=None): from .csg import CSGDifference return CSGDifference(self, geom, sampling_config) def __xor__(self, geom): return self.exclusive_or(geom) def exclusive_or(self, geom, sampling_config=None): from .csg import CSGXOR return CSGXOR(self, geom, sampling_config)
def _check_dict(part_sampling_dict): """check whether the type is dict""" if not isinstance(part_sampling_dict, dict): raise TypeError("part_sampling_dict should be type of dict, the key is geom_type while the value is " "instance of class PartSamplinlgConfig which describe the sampling info of each part, but got " "{} with type {}".format(part_sampling_dict, type(part_sampling_dict))) def _check_geom_type(part_sampling_dict): """check key and value of part_sampling_dict""" for geom_type in part_sampling_dict.keys(): if geom_type not in GEOM_TYPES: raise KeyError("Unsupported geom_type: {}, only {} are supported now".format(geom_type, GEOM_TYPES)) config = part_sampling_dict[geom_type] if not isinstance(config, PartSamplingConfig): raise TypeError("The type of config should be instance of class PartSamplingConfig, but got {} with type {}" .format(config, type(config))) def _check_size_type(geom): """check size type of specified geometry""" if geom is not None and not geom.random_sampling: if isinstance(geom.size, int) and not isinstance(geom.size, bool): geom.size = [geom.size] if not isinstance(geom.size, (list, tuple)): raise TypeError("The sample size should be type of list/tuple which length equal to the geom's " "dimension when sampling on regular mesh, but got {} with type {}" .format(geom.size, type(geom.size))) geom.size = np.array(geom.size).astype(np.int64) def _check_param_type(param, param_name, param_type): """check type of parameter""" if param_type == int: if isinstance(param, bool) or not isinstance(param, int): raise TypeError("The type of {} should be int but got {}".format(param_name, type(param))) else: if not isinstance(param, param_type): raise TypeError("The type of {} should be {} but got {}".format(param_name, param_type, type(param)))