mindflow.geometry.geometry_2d 源代码

# 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.
# ============================================================================
#pylint: disable=W0223
"""2d geometry"""

from __future__ import absolute_import
import numpy as np
import numpy.linalg as LA
from mindspore import log as logger

from .geometry_base import Geometry, DATA_TYPES, GEOM_TYPES, SamplingConfig
from .geometry_nd import HyperCube
from .geom_utils import sample, polar_sample, generate_mesh
from .shapes import adapter, simplex, pentagon, polygon
from ..utils.check_func import check_param_type, check_param_type_value

_SPACE = " "


[文档]class Disk(Geometry): r""" Definition of Disk object. Args: name (str): name of the disk. center (Union[tuple[int, int], tuple[float, float], list[int, int], list[float, float], numpy.ndarray]): center coordinates of the disk. radius (Union[int, float]): radius of the disk. dtype (numpy.dtype): data type of sampled point data type. Default: ``numpy.float32``. sampling_config (SamplingConfig): sampling configuration. Default: ``None``. Raises: ValueError: If `center` is neither list nor tuple of length 2. ValueError: If `radius` is negative. Supported Platforms: ``Ascend`` ``GPU`` Examples: >>> from mindflow.geometry import generate_sampling_config, Disk >>> disk_mesh = dict({'domain': dict({'random_sampling': False, 'size' : [100, 180]}), ... 'BC': dict({'random_sampling': False, 'size': 200, 'with_normal' : True,})}) >>> disk = Disk("disk", (-1.0, 0), 2.0, sampling_config=generate_sampling_config(disk_mesh)) >>> domain = disk.sampling(geom_type="domain") >>> bc, bc_normal = disk.sampling(geom_type="BC") >>> print(bc.shape) (200, 2) """ def __init__(self, name, center, radius, dtype=np.float32, sampling_config=None): self.sampling_config = sampling_config check_param_type(center, "center", data_type=(np.ndarray, tuple, list)) self.center = np.array(center) if len(self.center) != 2: raise ValueError("Disk: {}'s center position should be 2D array, but got {} with dim {}".format( name, self.center, len(self.center))) for ele in self.center: check_param_type(ele, "ele in center", data_type=DATA_TYPES, exclude_type=bool) check_param_type(radius, "radius", data_type=[int, float], exclude_type=bool) if radius <= 0: raise ValueError("Disk: {}'s radius should not be >=0.0, but got: {}".format(name, radius)) self.radius = radius self.columns_dict = {} coord_min = self.center - self.radius coord_max = self.center + self.radius super(Disk, self).__init__(name, 2, coord_min, coord_max, dtype, sampling_config) def _inside(self, points, strict=False): """whether inside domain""" return LA.norm(points - self.center, axis=-1) < self.radius if strict \ else LA.norm(points - self.center, axis=-1) <= self.radius def _on_boundary(self, points): """whether on domain boundary""" return np.isclose(LA.norm(points - self.center, axis=-1), self.radius) def _boundary_normal(self, points): """get the boundary normal vector""" points = points[self._on_boundary(points)] r = points - self.center r_norm = LA.norm(r, axis=-1, keepdims=True) return r / r_norm def _random_disk_boundary_points(self, need_normal=False): """Randomly generate boundary points""" size = self.sampling_config.bc.size sampler = self.sampling_config.bc.sampler theta = 2 * np.pi * sample(size, 1, sampler) circle_xy = np.hstack([np.cos(theta), np.sin(theta)]) data = self.center + circle_xy * self.radius data = np.reshape(data, (-1, self.dim)) if need_normal: normal_data = self._boundary_normal(data) normal_data = np.reshape(normal_data, (-1, self.dim)) return data, normal_data return data def _random_disk_domain_points(self): """Randomly generate domain points""" size = self.sampling_config.domain.size sampler = self.sampling_config.domain.sampler r_theta = sample(size, 2, sampler) data = self.center + polar_sample(r_theta) * self.radius data = np.reshape(data, (-1, self.dim)) return data def _grid_disk_boundary_points(self, need_normal=False): """Generate uniformly distributed domain points""" size = self.sampling_config.bc.size theta = np.linspace(0, 2 * np.pi, num=size, endpoint=False) cartesian = np.vstack((np.cos(theta), np.sin(theta))).T data = self.radius * cartesian + self.center data = np.reshape(data, (-1, self.dim)) if need_normal: normal_data = self._boundary_normal(data) normal_data = np.reshape(normal_data, (-1, self.dim)) return data, normal_data return data def _grid_disk_domain_points(self): """Generate uniformly distributed domain 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 )) r_theta_mesh = generate_mesh(np.array([0, 0]), np.array([1, 1]), mesh_size, endpoint=False) cartesian = np.zeros(r_theta_mesh.shape) cartesian[:, 0] = r_theta_mesh[:, 0] * self.radius * np.cos(2 * np.pi * r_theta_mesh[:, 1]) cartesian[:, 1] = r_theta_mesh[:, 0] * self.radius * np.sin(2 * np.pi * r_theta_mesh[:, 1]) data = cartesian + self.center data = np.reshape(data, (-1, self.dim)) return data def _get_sdf(self, domain_points): """calculate sign distance function""" sdf = self.radius - np.linalg.norm(domain_points - self.center, axis=1) return -sdf
[文档] def sampling(self, geom_type="domain"): """ sampling domain and boundary 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 check_param_type(config, _SPACE.join((self.geom_type, self.name, "'s sampling_config")), data_type=SamplingConfig) check_param_type_value(geom_type, _SPACE.join((self.geom_type, self.name, "'s geom_type")), GEOM_TYPES, data_type=str) if geom_type.lower() == "domain": check_param_type(config.domain, _SPACE.join((self.geom_type, self.name, "'s domain config")), exclude_type=type(None)) 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: disk_data = self._random_disk_domain_points() else: disk_data = self._grid_disk_domain_points() self.columns_dict["domain"] = [column_name] disk_data = disk_data.astype(self.dtype) if config.domain.with_sdf: sdf = self._get_sdf(disk_data) sdf = sdf.astype(self.dtype) sdf_column_name = self.name + "_domain_sdf" self.columns_dict.get("domain").append(sdf_column_name) return disk_data, sdf return disk_data if geom_type.lower() == "bc": check_param_type(config.bc, _SPACE.join((self.geom_type, self.name, "'s bc config")), exclude_type=type(None)) 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: disk_data, disk_data_normal = self._random_disk_boundary_points(need_normal=True) else: disk_data, disk_data_normal = self._grid_disk_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] disk_data = disk_data.astype(self.dtype) disk_data_normal = disk_data_normal.astype(self.dtype) return disk_data, disk_data_normal if config.bc.random_sampling: disk_data = self._random_disk_boundary_points(need_normal=False) else: disk_data = self._grid_disk_boundary_points(need_normal=False) column_data = self.name + "_BC_points" self.columns_dict["BC"] = [column_data] disk_data = disk_data.astype(self.dtype) return disk_data raise ValueError("Unknown geom_type: {}, only \"domain/BC\" are supported for {}:{}".format( geom_type, self.geom_type, self.name))
[文档]class Rectangle(HyperCube): r""" Definition of Rectangle object. Args: name (str): name of the rectangle. coord_min (Union[tuple[int, int], tuple[float, float], list[int, int], list[float, float], numpy.ndarray]): coordinates of the bottom left corner of rectangle. coord_max (Union[tuple[int, int], tuple[float, float], list[int, int], list[float, float], numpy.ndarray]): coordinates of the top right corner of rectangle. dtype (numpy.dtype): data type of sampled point data type. Default: ``numpy.float32``. sampling_config (SamplingConfig): sampling configuration. Default: ``None``. Supported Platforms: ``Ascend`` ``GPU`` Examples: >>> from mindflow.geometry import generate_sampling_config, Rectangle >>> rectangle_mesh = dict({'domain': dict({'random_sampling': False, 'size': [50, 25]}), ... 'BC': dict({'random_sampling': False, 'size': 300, 'with_normal': True,}),}) >>> rectangle = Rectangle("rectangle", (-3.0, 1), (1, 2), ... sampling_config=generate_sampling_config(rectangle_mesh)) >>> domain = rectangle.sampling(geom_type="domain") >>> bc, bc_normal = rectangle.sampling(geom_type="BC") >>> print(domain.shape) (1250, 2) """ def __init__(self, name, coord_min, coord_max, dtype=np.float32, sampling_config=None): super(Rectangle, self).__init__(name, 2, coord_min, coord_max, dtype=dtype, sampling_config=sampling_config)
[文档]class Triangle(adapter.Geometry): r""" Definition of triangle object. Args: name (str): name of the triangle. vertices (numpy.ndarray): vertices of the triangle. boundary_type (str): this can be ``'uniform'`` or ``'unweighted'``. Default: ``'uniform'``. - ``'uniform'``, the expected number of samples in each boundary is proportional to the area (length) of the boundary. - ``'unweighted'``, the expected number of samples in each boundary is the same. dtype (numpy.dtype): data type of sampled point data type. Default: ``np.float32``. sampling_config (SamplingConfig): sampling configuration. Default: ``none``. Supported Platforms: ``Ascend`` ``GPU`` Examples: >>> from mindflow.geometry import generate_sampling_config, Triangle >>> triangle_mesh = dict({'domain': dict({'random_sampling': True, 'size': 300}), ... 'BC': dict({'random_sampling': True, 'size': 300, 'with_normal': False,}),}) >>> vertices = np.array([[0., .1], [.9, .2], [.5, .6]]) >>> triangle = Triangle("triangle", vertices, ... sampling_config=generate_sampling_config(triangle_mesh)) >>> domain = triangle.sampling(geom_type="domain") >>> bc = triangle.sampling(geom_type="bc") >>> print(domain.shape) (300, 2) """ def __init__(self, name, vertices, boundary_type="uniform", dtype=np.float32, sampling_config=None): super(Triangle, self).__init__( name=name, shape=simplex.Simplex(vertices, boundary_type), dim=2, coord_min=np.min(vertices, axis=0), coord_max=np.max(vertices, axis=0), dtype=dtype, sampling_config=sampling_config, )
[文档]class Pentagon(adapter.Geometry): r""" Definition of pentagon object. Args: name (str): name of the pentagon. vertices (numpy.ndarray): vertices of the pentagon in an anti-clockwise order. boundary_type (str): this can be ``'uniform'`` or ``'unweighted'``. Default: ``'uniform'``. - ``'uniform'``, the expected number of samples in each boundary is proportional to the area (length) of the boundary. - ``'unweighted'``, the expected number of samples in each boundary is the same. dtype (numpy.dtype): data type of sampled point data type. Default: ``np.float32``. sampling_config (SamplingConfig): sampling configuration. Default: ``none``. Supported Platforms: ``Ascend`` ``GPU`` Examples: >>> from mindflow.geometry import generate_sampling_config, Pentagon >>> pentagon_mesh = dict({'domain': dict({'random_sampling': True, 'size': 300}), ... 'BC': dict({'random_sampling': True, 'size': 300, 'with_normal': False,}),}) >>> vertices = np.array([[0., .1], [.5, .1], [.9, .2], [.7, .6], [.2, .5]]) >>> pentagon = Pentagon("pentagon", vertices, ... sampling_config=generate_sampling_config(pentagon_mesh)) >>> domain = pentagon.sampling(geom_type="domain") >>> bc = pentagon.sampling(geom_type="bc") >>> print(domain.shape) (300, 2) """ def __init__(self, name, vertices, boundary_type="uniform", dtype=np.float32, sampling_config=None): super(Pentagon, self).__init__( name=name, shape=pentagon.Pentagon(vertices, boundary_type), dim=2, coord_min=np.min(vertices, axis=0), coord_max=np.max(vertices, axis=0), dtype=dtype, sampling_config=sampling_config, )
class Polygon(adapter.Geometry): r""" Definition of polygon object. Args: name (str): name of the polygon. vertices (numpy.ndarray): vertices of the polygon in an anti-clockwise order. boundary_type (str): this can be ``'uniform'`` or ``'unweighted'``. Default: ``'uniform'``. - ``'uniform'``, the expected number of samples in each boundary is proportional to the area (length) of the boundary. - ``'unweighted'``, the expected number of samples in each boundary is the same. dtype (numpy.dtype): data type of sampled point data type. Default: ``np.float32``. sampling_config (SamplingConfig): sampling configuration. Default: ``none``. Supported Platforms: ``Ascend`` ``GPU`` Examples: >>> from mindflow.geometry import generate_sampling_config, Polygon >>> polygon_mesh = dict({'domain': dict({'random_sampling': True, 'size': 300}), ... 'BC': dict({'random_sampling': True, 'size': 300, 'with_normal': False,}),}) >>> vertices = np.array([[0., 0], [1, 0], [1, 1], [.5, 1], [0.5, 0.5], [0, 0.5]]) >>> polygon = Polygon("polygon", vertices, ... sampling_config=generate_sampling_config(polygon_mesh)) >>> domain = polygon.sampling(geom_type="domain") >>> bc = polygon.sampling(geom_type="bc") >>> print(domain.shape) (300, 2) """ def __init__(self, name, vertices, boundary_type="uniform", dtype=np.float32, sampling_config=None): super(Polygon, self).__init__( name=name, shape=polygon.Polygon(vertices, boundary_type), dim=2, coord_min=np.min(vertices, axis=0), coord_max=np.max(vertices, axis=0), dtype=dtype, sampling_config=sampling_config, )