-
Notifications
You must be signed in to change notification settings - Fork 7
FunctionalBlock #153
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
base: release
Are you sure you want to change the base?
FunctionalBlock #153
Changes from 11 commits
bdbe08e
c5a82e0
5a0fb48
bd9c33b
013c5f8
74ccf80
8111583
8449d7d
9f75c4c
312c447
3d15fee
c477bd0
0a91fb5
ac856a0
601f6d4
198df54
e38875d
8db851a
a7854d6
3eff909
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,7 +15,7 @@ | |
| from itertools import chain | ||
| import numpy | ||
|
|
||
| from FIAT import polynomial_set, jacobi, quadrature_schemes | ||
| from FIAT import polynomial_set, jacobi, quadrature, quadrature_schemes | ||
|
|
||
|
|
||
| def index_iterator(shp): | ||
|
|
@@ -25,7 +25,127 @@ def index_iterator(shp): | |
| return numpy.ndindex(shp) | ||
|
|
||
|
|
||
| class Functional(object): | ||
| def pullback(mapping, J, detJ, phi): | ||
| try: | ||
| formdegree = { | ||
| "identity": (0,), | ||
| "L2 piola": (3,), | ||
| "covariant piola": (1,), | ||
| "contravariant piola": (2,), | ||
| "double covariant piola": (1, 1), | ||
| "double contravariant piola": (2, 2), | ||
| "covariant contravariant piola": (1, 2), | ||
| "contravariant covariant piola": (2, 1), }[mapping] | ||
| except KeyError: | ||
| raise ValueError(f"Unrecognized mapping {mapping}") | ||
|
|
||
| F1 = numpy.linalg.pinv(J).T | ||
| F2 = J / detJ | ||
|
|
||
| perm = [*range(1, phi.ndim-1), 0, -1] | ||
| phi = phi.transpose(perm) | ||
|
|
||
| for i, k in enumerate(formdegree): | ||
| if k == 0: | ||
| continue | ||
| elif k == 3: | ||
| phi = phi / detJ | ||
| else: | ||
| F = F1 if k == 1 else F2 | ||
| phi = numpy.tensordot(F, phi, (1, i)) | ||
|
|
||
| perm = [-2, *reversed(range(phi.ndim-2)), -1] | ||
| phi = phi.transpose(perm) | ||
| return phi | ||
|
|
||
|
|
||
| class FunctionalBlock: | ||
| def __init__(self, nodes): | ||
| self.nodes = nodes | ||
|
|
||
| def __iter__(self): | ||
| for ell in self.nodes: | ||
| yield ell | ||
|
|
||
| def __len__(self): | ||
| return len(self.nodes) | ||
|
|
||
| def completion(self): | ||
| return self | ||
|
|
||
|
|
||
| class FunctionalBlockView(FunctionalBlock): | ||
|
pbrubeck marked this conversation as resolved.
|
||
| def __init__(self, block, index): | ||
| self.block = block | ||
| nodes = [block.nodes[index]] | ||
| super().__init__(nodes) | ||
|
|
||
| def completion(self): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Commentary: For later with zany map. |
||
| return self.block | ||
|
|
||
|
|
||
| class PointFunctionalBlock(FunctionalBlock): | ||
| def __init__(self, ref_el, x, order): | ||
| from FIAT.polynomial_set import mis | ||
| sd = ref_el.get_spatial_dimension() | ||
| nodes = [PointDerivative(ref_el, x, alpha) for alpha in mis(sd, order)] | ||
| super().__init__(nodes) | ||
|
|
||
|
|
||
| class PointGradient(PointFunctionalBlock): | ||
| def __init__(self, ref_el, x): | ||
| super().__init__(ref_el, x, 1) | ||
|
|
||
|
|
||
| class PointHessian(PointFunctionalBlock): | ||
| def __init__(self, ref_el, x): | ||
| super().__init__(ref_el, x, 2) | ||
|
|
||
|
|
||
| class PointDirectionalDerivativeBlock(PointFunctionalBlock): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we generalize the "view" idea to capture that this is a linear combination of the pieces of the gradient? |
||
| def __init__(self, ref_el, x, basis): | ||
| nodes = [PointDirectionalDerivative(ref_el, x, e) for e in basis] | ||
| super().__init__(nodes) | ||
|
|
||
|
|
||
| class PointNormalTangentialDerivativeBlock(PointDirectionalDerivativeBlock): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. More generally: Gradient block doesn't require a particular basis. Is this and the gradient instances of a common abstract gradient?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the Cartesian case is special, since it is the only one we can use to enforce continuity of derivatives at a vertex |
||
| def __init__(self, ref_el, x, facet_id): | ||
| sd = ref_el.get_spatial_dimension() | ||
| basis = numpy.zeros((sd, sd)) | ||
| basis[0] = ref_el.compute_scaled_normal(facet_id) | ||
| basis[1:] = ref_el.compute_tangents(sd-1, facet_id) | ||
| super().__init__(self, x, basis) | ||
|
|
||
|
|
||
| class PointNormalDerivativeView(FunctionalBlockView): | ||
| def __init__(self, ref_el, x, facet_id): | ||
| block = PointNormalTangentialDerivativeBlock(ref_el, x, facet_id) | ||
| super().__init__(block, 0) | ||
|
|
||
|
|
||
| class FacetIntegralMomentBlock(FunctionalBlock): | ||
| def __init__(self, ref_el, entity_dim, entity_id, Q_ref, Phis, mapping="L2 piola"): | ||
| Q = quadrature.FacetQuadratureRule(ref_el, entity_dim, entity_id, Q_ref) | ||
| phis = pullback(mapping, Q.jacobian(), Q.jacobian_determinant(), Phis) | ||
| nodes = [FrobeniusIntegralMoment(ref_el, Q, phi) for phi in phis] | ||
| super().__init__(nodes) | ||
|
|
||
|
|
||
| class FacetDirectionalIntegralMomentBlock(FacetIntegralMomentBlock): | ||
| def __init__(self, ref_el, entity_dim, entity_id, Q_ref, Phis, direction): | ||
| direction = numpy.asarray(direction) | ||
| Phis = Phis[(slice(None), *(None for _ in range(direction.ndim)), slice(None))] * direction[(*(None for _ in range(Phis.ndim-1)), Ellipsis, None)] | ||
| super().__init__(ref_el, entity_dim, entity_id, Q_ref, Phis) | ||
|
|
||
|
|
||
| class FacetNormalIntegralMomentBlock(FacetDirectionalIntegralMomentBlock): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Somehow (later) there are pieces of this that want attention so we can automated H(div) zany things (MTW, AW) |
||
| def __init__(self, ref_el, facet_id, Q_ref, Phis): | ||
| direction = ref_el.compute_scaled_normal(facet_id) | ||
| sd = ref_el.get_spatial_dimension() | ||
| super().__init__(ref_el, sd-1, facet_id, Q_ref, Phis, direction) | ||
|
|
||
|
|
||
| class Functional(FunctionalBlock): | ||
| r"""Abstract class representing a linear functional. | ||
| All FIAT functionals are discrete in the sense that | ||
| they are written as a weighted sum of (derivatives of components of) their | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this means when we
extenda list of functionals with a block, that we just get back all of the functionals in the block.