qbhf2's picture
added NvidiaWarp and GarmentCode repos
66c9c8a
import inspect
from typing import Callable, Any
import warp as wp
from warp.fem.types import Domain, Field, Sample
from warp.fem import utils
class Integrand:
"""An integrand is a device function containing arbitrary expressions over Field and Domain variables.
It will get transformed to a proper warp.Function by resolving concrete Field types at call time.
"""
def __init__(self, func: Callable):
self.func = func
self.name = wp.codegen.make_full_qualified_name(self.func)
self.module = wp.get_module(self.func.__module__)
self.argspec = inspect.getfullargspec(self.func)
class Operator:
"""
Operators provide syntaxic sugar over Field and Domain evaluation functions and arguments
"""
def __init__(self, func: Callable, resolver: Callable):
self.func = func
self.resolver = resolver
def integrand(func: Callable):
"""Decorator for functions to be integrated (or interpolated) using warp.fem"""
itg = Integrand(func)
itg.__doc__ = func.__doc__
return itg
def operator(resolver: Callable):
"""Decorator for functions operating on Field-like or Domain-like data inside warp.fem integrands"""
def wrap_operator(func: Callable):
op = Operator(func, resolver)
op.__doc__ = func.__doc__
return op
return wrap_operator
# Domain operators
@operator(resolver=lambda dmn: dmn.element_position)
def position(domain: Domain, s: Sample):
"""Evaluates the world position of the sample point `s`"""
pass
@operator(resolver=lambda dmn: dmn.eval_normal)
def normal(domain: Domain, s: Sample):
"""Evaluates the element normal at the sample point `s`. Null for interior points."""
pass
@operator(resolver=lambda dmn: dmn.element_deformation_gradient)
def deformation_gradient(domain: Domain, s: Sample):
"""Evaluates the gradient of the domain position with respect to the element reference space at the sample point `s`"""
pass
@operator(resolver=lambda dmn: dmn.element_lookup)
def lookup(domain: Domain, x: Any) -> Sample:
"""Looks-up the sample point corresponding to a world position `x`, projecting to the closest point on the domain.
Arg:
x: world position of the point to look-up in the geometry
guess: (optional) :class:`Sample` initial guess, may help perform the query
Notes:
Currently this operator is only fully supported for :class:`Grid2D` and :class:`Grid3D` geometries.
For :class:`TriangleMesh2D` and :class:`Tetmesh` geometries, the operator requires providing `guess`.
"""
pass
@operator(resolver=lambda dmn: dmn.element_measure)
def measure(domain: Domain, s: Sample) -> float:
"""Returns the measure (volume, area, or length) determinant of an element at a sample point `s`"""
pass
@operator(resolver=lambda dmn: dmn.element_measure_ratio)
def measure_ratio(domain: Domain, s: Sample) -> float:
"""Returns the maximum ratio between the measure of this element and that of higher-dimensional neighbours."""
pass
# Field operators
# On a side, inner and outer are such that normal goes from inner to outer
@operator(resolver=lambda f: f.eval_inner)
def inner(f: Field, s: Sample):
"""Evaluates the field at a sample point `s`. On oriented sides, uses the inner element"""
pass
@operator(resolver=lambda f: f.eval_grad_inner)
def grad(f: Field, s: Sample):
"""Evaluates the field gradient at a sample point `s`. On oriented sides, uses the inner element"""
pass
@operator(resolver=lambda f: f.eval_div_inner)
def div(f: Field, s: Sample):
"""Evaluates the field divergence at a sample point `s`. On oriented sides, uses the inner element"""
pass
@operator(resolver=lambda f: f.eval_outer)
def outer(f: Field, s: Sample):
"""Evaluates the field at a sample point `s`. On oriented sides, uses the outer element. On interior points and on domain boundaries, this is equivalent to :func:`inner`."""
pass
@operator(resolver=lambda f: f.eval_grad_outer)
def grad_outer(f: Field, s: Sample):
"""Evaluates the field gradient at a sample point `s`. On oriented sides, uses the outer element. On interior points and on domain boundaries, this is equivalent to :func:`grad`."""
pass
@operator(resolver=lambda f: f.eval_grad_outer)
def div_outer(f: Field, s: Sample):
"""Evaluates the field divergence at a sample point `s`. On oriented sides, uses the outer element. On interior points and on domain boundaries, this is equivalent to :func:`div`."""
pass
@operator(resolver=lambda f: f.eval_degree)
def degree(f: Field):
"""Polynomial degree of a field"""
pass
@operator(resolver=lambda f: f.at_node)
def at_node(f: Field, s: Sample):
"""For a Test or Trial field, returns a copy of the Sample `s` moved to the coordinates of the node being evaluated"""
pass
# Common derived operators, for convenience
@integrand
def D(f: Field, s: Sample):
"""Symmetric part of the (inner) gradient of the field at `s`"""
return utils.symmetric_part(grad(f, s))
@integrand
def curl(f: Field, s: Sample):
"""Skew part of the (inner) gradient of the field at `s`, as a vector such that ``wp.cross(curl(u), v) = skew(grad(u)) v``"""
return utils.skew_part(grad(f, s))
@integrand
def jump(f: Field, s: Sample):
"""Jump between inner and outer element values on an interior side. Zero for interior points or domain boundaries"""
return inner(f, s) - outer(f, s)
@integrand
def average(f: Field, s: Sample):
"""Average between inner and outer element values"""
return 0.5 * (inner(f, s) + outer(f, s))
@integrand
def grad_jump(f: Field, s: Sample):
"""Jump between inner and outer element gradients on an interior side. Zero for interior points or domain boundaries"""
return grad(f, s) - grad_outer(f, s)
@integrand
def grad_average(f: Field, s: Sample):
"""Average between inner and outer element gradients"""
return 0.5 * (grad(f, s) + grad_outer(f, s))
# Set default call operators for argument types, so that field(s) = inner(field, s) and domain(s) = position(domain, s)
Field.call_operator = inner
Domain.call_operator = position