Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【PPSCI Doc No.66-74】 #829

Merged
merged 12 commits into from
Apr 4, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
213 changes: 197 additions & 16 deletions ppsci/geometry/timedomain.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
from __future__ import annotations

import itertools
from typing import Callable
from typing import Dict
from typing import Optional
from typing import Tuple

Expand Down Expand Up @@ -60,17 +62,37 @@ def __init__(
self.t0 = t0
self.t1 = t1
self.time_step = time_step
self.timestamps = np.array(
timestamps, dtype=paddle.get_default_dtype()
).reshape([-1])
if timestamps is None:
self.timestamps = None
else:
self.timestamps = np.array(
timestamps, dtype=paddle.get_default_dtype()
).reshape([-1])
if time_step is not None:
if time_step <= 0:
raise ValueError(f"time_step({time_step}) must be larger than 0.")
self.num_timestamps = int(np.ceil((t1 - t0) / time_step)) + 1
elif timestamps is not None:
self.num_timestamps = len(timestamps)

def on_initial(self, t):
def on_initial(self, t: np.ndarray):
"""Check if a specific time is on the initial time point.

Args:
t (np.ndarray): The time to be checked.

Returns:
bool: True or False for whether the specific time is on the initial time point.

Examples:
>>> import paddle
>>> import ppsci
>>> geom = ppsci.geometry.TimeDomain(0, 1)
>>> T = [0, 0.01, 0.126, 0.2, 0.3]
>>> check = geom.on_initial(T)
>>> print(check)
[ True False False False False]
"""
return np.isclose(t, self.t0).flatten()


Expand Down Expand Up @@ -110,11 +132,26 @@ def boundary_normal(self, x):
normal = self.geometry.boundary_normal(x[:, 1:])
return np.hstack((x[:, :1], normal))

def uniform_points(self, n, boundary=True):
def uniform_points(self, n: int, boundary: bool = True):
"""Uniform points on the spatial-temporal domain.

Geometry volume ~ bbox.
Time volume ~ diam.

Args:
n (int): The total number of sample points to be generated.
boundary (bool): Indicates whether boundary points are included, default is True.

Returns:
np.ndarray: a set of spatial-temporal coordinate points 'tx' that represent sample points evenly distributed within the spatial-temporal domain.

Examples:
>>> import ppsci
>>> timedomain = ppsci.geometry.TimeDomain(0, 1, 0.001)
>>> geom = ppsci.geometry.Rectangle((0, 0), (1, 1))
>>> time_geom = ppsci.geometry.TimeXGeometry(timedomain, geom)
>>> ts = time_geom.uniform_points(1000)
>>> print(ts.shape)
(1000, 3)
"""
if self.timedomain.time_step is not None:
# exclude start time t0
Expand Down Expand Up @@ -163,7 +200,30 @@ def uniform_points(self, n, boundary=True):
tx = tx[:n]
return tx

def random_points(self, n, random="pseudo", criteria=None):
def random_points(
self, n: int, random: str = "pseudo", criteria: Optional[Callable] = None
):
"""Generate random points on the spatial-temporal domain.

Args:
n (int): The total number of random points to generate.
random (string): Specifies the way to generate random points, default is "pseudo" , which means that a pseudo-random number generator is used.
criteria (Optional[Callable]): A method that filters on the generated random points, defualt is None.

Returns:
np.ndarray: A set of random spatial-temporal points.

Examples:
>>> import ppsci
>>> timedomain = ppsci.geometry.TimeDomain(0, 1, 0.001)
>>> geom = ppsci.geometry.Rectangle((0, 0), (1, 1))
>>> time_geom = ppsci.geometry.TimeXGeometry(timedomain, geom)
>>> ts = time_geom.random_points(1000)
>>> print(ts.shape)
(1000, 3)
"""
if self.timedomain.time_step is None and self.timedomain.timestamps is None:
raise ValueError("Either time_step or timestamps must be provided.")
# time evenly and geometry random, if time_step if specified
if self.timedomain.time_step is not None:
nt = int(np.ceil(self.timedomain.diam / self.timedomain.time_step))
Expand Down Expand Up @@ -287,11 +347,26 @@ def random_points(self, n, random="pseudo", criteria=None):
t = np.random.permutation(t)
return np.hstack((t, x))

def uniform_boundary_points(self, n, criteria=None):
def uniform_boundary_points(self, n: int, criteria: Optional[Callable] = None):
"""Uniform boundary points on the spatial-temporal domain.

Geometry surface area ~ bbox.
Time surface area ~ diam.

Args:
n (int): The total number of boundary points on the spatial-temporal domain to be generated that are evenly distributed across geometry boundaries.
criteria (Optional[Callable]): Used to filter the generated boundary points, only points that meet certain conditions are retained. Default is None.

Returns:
np.ndarray: A set of point coordinates evenly distributed across geometry boundaries on the spatial-temporal domain.

Examples:
>>> import ppsci
>>> timedomain = ppsci.geometry.TimeDomain(0, 1)
>>> geom = ppsci.geometry.Rectangle((0, 0), (1, 1))
>>> time_geom = ppsci.geometry.TimeXGeometry(timedomain, geom)
>>> ts = time_geom.uniform_boundary_points(1000)
>>> print(ts.shape)
(1000, 3)
"""
if self.geometry.ndim == 1:
nx = 2
Expand Down Expand Up @@ -350,7 +425,30 @@ def uniform_boundary_points(self, n, criteria=None):
tx = tx[:n]
return tx

def random_boundary_points(self, n, random="pseudo", criteria=None):
def random_boundary_points(
self, n: int, random: str = "pseudo", criteria: Optional[Callable] = None
):
"""Random boundary points on the spatial-temporal domain.

Args:
n (int): The total number of spatial-temporal points generated on a given geometry boundary.
random (string): Controls the way to generate random points. Default is "pseudo".
criteria (Optional[Callable]): Used to filter the generated boundary points, only points that meet certain conditions are retained. Default is None.

Returns:
np.ndarray: A set of point coordinates randomly distributed across geometry boundaries on the spatial-temporal domain.

Examples:
>>> import ppsci
>>> timedomain = ppsci.geometry.TimeDomain(0, 1, 0.001)
>>> geom = ppsci.geometry.Rectangle((0, 0), (1, 1))
>>> time_geom = ppsci.geometry.TimeXGeometry(timedomain, geom)
>>> ts = time_geom.random_boundary_points(1000)
>>> print(ts.shape)
(1000, 3)
"""
if self.timedomain.time_step is None and self.timedomain.timestamps is None:
raise ValueError("Either time_step or timestamps must be provided.")
if self.timedomain.time_step is not None:
# exclude start time t0
nt = int(np.ceil(self.timedomain.diam / self.timedomain.time_step))
Expand Down Expand Up @@ -523,19 +621,78 @@ def random_boundary_points(self, n, random="pseudo", criteria=None):
else:
return t_x

def uniform_initial_points(self, n):
def uniform_initial_points(self, n: int):
"""Generate evenly distributed point coordinates on the spatial-temporal domain at the initial moment.

Args:
n (int): The total number of generated points.

Returns:
np.ndarray: A set of point coordinates evenly distributed on the spatial-temporal domain at the initial moment.

Examples:
>>> import ppsci
>>> timedomain = ppsci.geometry.TimeDomain(0, 1)
>>> geom = ppsci.geometry.Rectangle((0, 0), (1, 1))
>>> time_geom = ppsci.geometry.TimeXGeometry(timedomain, geom)
>>> ts = time_geom.uniform_initial_points(1000)
>>> print(ts.shape)
(1000, 3)
"""
x = self.geometry.uniform_points(n, True)
t = self.timedomain.t0
if len(x) > n:
x = x[:n]
return np.hstack((np.full([n, 1], t, dtype=paddle.get_default_dtype()), x))

def random_initial_points(self, n, random="pseudo"):
def random_initial_points(self, n: int, random: str = "pseudo"):
"""Generate randomly distributed point coordinates on the spatial-temporal domain at the initial moment.

Args:
n (int): The total number of generated points.
random (string): Controls the way to generate random points. Default is "pseudo".

Returns:
np.ndarray: A set of point coordinates randomly distributed on the spatial-temporal domain at the initial moment.

Examples:
>>> import ppsci
>>> timedomain = ppsci.geometry.TimeDomain(0, 1)
>>> geom = ppsci.geometry.Rectangle((0, 0), (1, 1))
>>> time_geom = ppsci.geometry.TimeXGeometry(timedomain, geom)
>>> ts = time_geom.random_initial_points(1000)
>>> print(ts.shape)
(1000, 3)
"""
x = self.geometry.random_points(n, random=random)
t = self.timedomain.t0
return np.hstack((np.full([n, 1], t, dtype=paddle.get_default_dtype()), x))

def periodic_point(self, x, component):
def periodic_point(self, x: Dict[str, np.ndarray], component: int):
"""process given point coordinates to satisfy the periodic boundary conditions of the geometry.

Args:
x (Dict[str, np.ndarray]): Contains the coordinates and timestamps of the points. It represents the coordinates of the point to be processed.
component (int): Specifies the components or dimensions of specific spatial coordinates that are periodically processed.

Returns:
Dict[str, np.ndarray] : contains the original timestamps and the coordinates of the spatial point after periodic processing.

Examples:
>>> import ppsci
>>> timedomain = ppsci.geometry.TimeDomain(0, 1, 0.1)
>>> geom = ppsci.geometry.Rectangle((0, 0), (1, 1))
>>> time_geom = ppsci.geometry.TimeXGeometry(timedomain, geom)
>>> ts = time_geom.sample_boundary(1000)
>>> result = time_geom.periodic_point(ts, 0)
>>> for k,v in result.items():
... print(k, v.shape)
t (1000, 1)
x (1000, 1)
y (1000, 1)
normal_x (1000, 1)
normal_y (1000, 1)
"""
xp = self.geometry.periodic_point(x, component)
txp = {"t": x["t"], **xp}
return txp
Expand All @@ -544,11 +701,35 @@ def sample_initial_interior(
self,
n: int,
random: str = "pseudo",
criteria=None,
evenly=False,
criteria: Optional[Callable] = None,
evenly: bool = False,
compute_sdf_derivatives: bool = False,
):
"""Sample random points in the time-geometry and return those meet criteria."""
"""Sample random points in the time-geometry and return those meet criteria.

Args:
n (int): The total number of interior points generated.
random (string): The method used to specify the initial point of generation. Default is "pseudo".
criteria (Optional[Callable]): Used to filter the generated interior points, only points that meet certain conditions are retained. Default is None.
evenly (bool): Indicates whether the initial points are generated evenly. Default is False.
compute_sdf_derivatives (bool): Indicates whether to calculate the derivative of signed distance function or not. Default is False.

Returns:
np.ndarray: Contains the coordinates of the initial internal point generated, as well as the potentially computed signed distance function and its derivative.

Examples:
>>> import ppsci
>>> timedomain = ppsci.geometry.TimeDomain(0, 1)
>>> geom = ppsci.geometry.Rectangle((0, 0), (1, 1))
>>> time_geom = ppsci.geometry.TimeXGeometry(timedomain, geom)
>>> ts = time_geom.sample_initial_interior(1000)
>>> for k,v in ts.items():
... print(k, v.shape)
t (1000, 1)
x (1000, 1)
y (1000, 1)
sdf (1000, 1)
"""
x = np.empty(shape=(n, self.ndim), dtype=paddle.get_default_dtype())
_size, _ntry, _nsuc = 0, 0, 0
while _size < n:
Expand Down