# 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.
# ============================================================================
"""3d geometry"""
from __future__ import absolute_import
import numpy as np
from mindspore import log as logger
from .geometry_base import Geometry, GEOM_TYPES
from .utils import sample, generate_mesh
[文档]class HyperCube(Geometry):
r"""
Definition of HyperCube object.
Args:
name (str): name of the hyper cube.
dim (int): number of dimensions.
coord_min (Union[int, float, tuple, list, numpy.ndarray]): minimal coordinate of the hyper cube. if the
parameter type is tuple or list, the element support tuple[int, int], tuple[float, float], list[int, int],
list[float, float].
coord_max (Union[int, float, tuple, list, numpy.ndarray]): maximal coordinate of the hyper cube. if the
parameter type is tuple or list, the element support tuple[int, int], tuple[float, float], list[int, int],
list[float, float].
dtype (numpy.dtype): Data type of sampled point data type. Default: numpy.float32.
sampling_config (SamplingConfig): sampling configuration. Default: None.
Raises:
TypeError: sampling_config is not instance of class SamplingConfig.
Supported Platforms:
``Ascend``
Examples:
>>> from easydict import EasyDict as edict
>>> from mindelec.geometry import create_config_from_edict, HyperCube
>>> hypercube_random = edict({
... 'domain': edict({
... 'random_sampling': True,
... 'size': 1000,
... 'sampler': 'uniform'
... }),
... 'BC': edict({
... 'random_sampling': True,
... 'size': 200,
... 'sampler': 'uniform',
... 'with_normal': False,
... }),
... })
>>> sampling_config = create_config_from_edict(hypercube_random)
>>> hypercube = HyperCube("HyperCube", 3, [-1, 2, 1], [0, 3, 2], sampling_config=sampling_config)
>>> domain = hypercube.sampling(geom_type="domain")
>>> bc = hypercube.sampling(geom_type="BC")
>>> print(domain.shape)
(1000, 3)
"""
def __init__(self, name, dim, coord_min, coord_max, dtype=np.float32, sampling_config=None):
super(HyperCube, self).__init__(name, dim, coord_min, coord_max, dtype, sampling_config)
self.columns_dict = {}
self.length = self.coord_max - self.coord_min
self.vol = np.prod(self.length)
area = 0
for i in range(dim):
area += self.vol / self.length[i]
self.area = area * 2.0
def _inside(self, points, strict=False):
"""whether inside domain"""
valid_min = (self.coord_min < points).all(axis=-1) if strict else (self.coord_min <= points).all(axis=-1)
valid_max = (self.coord_max > points).all(axis=-1) if strict else (self.coord_max >= points).all(axis=-1)
return np.logical_and(valid_min, valid_max)
def _on_boundary(self, points):
"""whether on geometry's boundary"""
near_boundary = np.logical_or(np.any(np.isclose(points, self.coord_min), axis=-1),
np.any(np.isclose(points, self.coord_max), axis=-1))
return near_boundary
def _boundary_normal(self, points):
"""get the normal vector of boundary points"""
points = self._filter_corner_points(points)
normal = np.isclose(points, self.coord_min) * -1.0 + np.isclose(points, self.coord_max) * 1.0
return normal
def _filter_corner_points(self, points):
corner = np.isclose(points, self.coord_min) + np.isclose(points, self.coord_max)
have_corner = np.count_nonzero(corner, axis=-1)
not_corner_points = np.where(have_corner <= 1)[0]
return points[not_corner_points]
def _random_domain_points(self):
"""randomly generate domain points"""
size = self.sampling_config.domain.size
sampler = self.sampling_config.domain.sampler
data = sample(size, self.dim, sampler) * (self.coord_max - self.coord_min) + self.coord_min
data = np.reshape(data, (-1, self.dim))
return data
def _grid_domain_points(self):
"""generate domain mesh points"""
mesh_size = self.sampling_config.domain.size
if len(mesh_size) != self.dim:
raise ValueError("For grid sampling, length of mesh_size list: {} should be equal to dimension: {}".format(
mesh_size, self.dim
))
mesh_x = generate_mesh(self.coord_min, self.coord_max, mesh_size)
data = np.reshape(mesh_x, (-1, self.dim))
return data
def _random_boundary_points(self, need_normal=False):
"""get boundary points randomly"""
size = self.sampling_config.bc.size
sampler = self.sampling_config.bc.sampler
boundary_points = []
for i in range(self.dim):
area_i = self.vol / self.length[i]
ratio = area_i * 2 / self.area
num_sample = int(size * ratio)
temp_boundary_points = sample(num_sample, self.dim, sampler)
temp_boundary_points[np.arange(num_sample), i] = np.round(temp_boundary_points[np.arange(num_sample), i])
boundary_points.extend([temp_boundary_points])
data = np.concatenate(boundary_points, axis=0)
data = np.random.permutation(data)
data = data * (self.coord_max - self.coord_min) + self.coord_min
if need_normal:
data = self._filter_corner_points(data)
data = np.reshape(data, (-1, self.dim))
data_normal = self._boundary_normal(data)
data_normal = np.reshape(data_normal, (-1, self.dim))
return data, data_normal
data = np.reshape(data, (-1, self.dim))
return data
def _grid_boundary_points(self, need_normal=False):
"""get gird boundary points"""
size = self.sampling_config.bc.size
if self.dim == 1:
ds = self.area / 5.0
else:
ds = (self.area / size) ** (1 / (self.dim - 1))
mesh_size = (self.length / ds).astype(np.int64)
domain_data = generate_mesh(self.coord_min, self.coord_max, mesh_size)
bound_cond = np.where(self._on_boundary(domain_data))[0]
data = domain_data[bound_cond]
data = np.reshape(data, (-1, self.dim))
data = np.random.permutation(data)
if need_normal:
data = self._filter_corner_points(data)
data_normal = self._boundary_normal(data)
data_normal = np.reshape(data_normal, (-1, self.dim))
return data, data_normal
return data
[文档] def sampling(self, geom_type="domain"):
"""
sampling points
Args:
geom_type (str): geometry type: can be 'domain' or 'BC'. Default: 'domain'.
- 'domain', feasible domain of the problem.
- 'BC', boundary of the problem.
Returns:
Numpy.array, if the with_normal property of boundary configuration is true, returns 2D numpy array with
boundary normal vectors. Otherwise, returns 2D numpy array without boundary normal vectors.
Raises:
ValueError: If `config` is None.
KeyError: If `geom_type` is `domain` but `config.domain` is None.
KeyError: If `geom_type` is `BC` but `config.bc` is None.
ValueError: If `geom_type` is neither `BC` nor `domain`.
"""
config = self.sampling_config
if config is None:
raise ValueError("Sampling config for {}:{} is None, please call set_sampling_config method to set".format(
self.geom_type, self.name))
if not isinstance(geom_type, str):
raise TypeError("geom_type shouild be string, but got {} with type {}".format(geom_type, type(geom_type)))
if geom_type not in GEOM_TYPES:
raise ValueError("Unsupported geom_type: {}, only {} are supported now".format(geom_type, GEOM_TYPES))
if geom_type.lower() == "domain":
if config.domain is None:
raise KeyError("Sampling config for domain of {}:{} should not be none"
.format(self.geom_type, self.name))
logger.info("Sampling domain points for {}:{}, config info: {}"
.format(self.geom_type, self.name, config.domain))
column_name = self.name + "_domain_points"
if config.domain.random_sampling:
data = self._random_domain_points()
else:
data = self._grid_domain_points()
self.columns_dict["domain"] = [column_name]
data = data.astype(self.dtype)
return data
if geom_type.lower() == "bc":
if config.bc is None:
raise KeyError("Sampling config for BC of {}:{} should not be none".format(self.geom_type, self.name))
logger.info("Sampling BC points for {}:{}, config info: {}"
.format(self.geom_type, self.name, config.bc))
if config.bc.with_normal:
if config.bc.random_sampling:
data, data_normal = self._random_boundary_points(need_normal=True)
else:
data, data_normal = self._grid_boundary_points(need_normal=True)
column_data = self.name + "_BC_points"
column_normal = self.name + "_BC_normal"
self.columns_dict["BC"] = [column_data, column_normal]
data = data.astype(self.dtype)
data_normal = data_normal.astype(self.dtype)
return data, data_normal
if config.bc.random_sampling:
data = self._random_boundary_points(need_normal=False)
else:
data = self._grid_boundary_points(need_normal=False)
column_data = self.name + "_BC_points"
self.columns_dict["BC"] = [column_data]
data = data.astype(self.dtype)
return data
raise ValueError("Unknown geom_type: {}, only \"domain/BC\" are supported for {}:{}".format(
geom_type, self.geom_type, self.name))