fuse-element 0.1.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,138 @@
1
+ from functools import total_ordering
2
+
3
+
4
+ @total_ordering
5
+ class InterpolationSpace(object):
6
+ """Symbolic representation of an interpolation function space
7
+
8
+ This implements a subset of the methods of a Python set so that
9
+ other spaces can be tested for inclusion.
10
+ """
11
+
12
+ def __init__(self, name, shape=None, parents=None):
13
+ """Instantiate a InterpolationSpace object.
14
+
15
+ Args:
16
+ name: The name of this space,
17
+ parents: A set of spaces of which this
18
+ space is a subspace.
19
+ """
20
+ self.name = name
21
+ p = frozenset(parents or [])
22
+ # Ensure that the inclusion operations are transitive.
23
+ self.parents = p.union(*[p_.parents for p_ in p])
24
+
25
+ def __str__(self):
26
+ """Format as a string."""
27
+ return self.name
28
+
29
+ def __repr__(self):
30
+ """Representation."""
31
+ return f"InterpolationSpace({self.name!r}, {list(self.parents)!r})"
32
+
33
+ def __eq__(self, other):
34
+ """Check equality."""
35
+ return isinstance(other, InterpolationSpace) and self.name == other.name and self.shape == other.shape
36
+
37
+ def __ne__(self, other):
38
+ """Not equal."""
39
+ return not self == other
40
+
41
+ def __hash__(self):
42
+ """Hash."""
43
+ return hash(("InterpolationSpace", self.name))
44
+
45
+ def __lt__(self, other):
46
+ """In common with intrinsic Python sets, < indicates "is a proper subset of"."""
47
+ return other in self.parents
48
+
49
+ def _to_dict(self):
50
+ return {"space": str(self)}
51
+
52
+ def dict_id(self):
53
+ return "InterpolationSpace"
54
+
55
+ def _from_dict(obj_dict):
56
+ return InterpolationSpace(obj_dict["space"])
57
+
58
+
59
+ class Sobolev(InterpolationSpace):
60
+ """
61
+ Describes the Sobolev Space W_m,p
62
+
63
+ Arguments:
64
+ - Derivatives (m): the numbers of derivatives that are required to exist
65
+ - Lebesgue (p): The L_p space the derivatives are required to be in
66
+ """
67
+
68
+ def __init__(self, derivatives, lebesgue, shape=None, name=None, parents=[]):
69
+ self.derivatives = derivatives
70
+ self.lebesgue = lebesgue
71
+ self.shape = shape
72
+
73
+ if name is None:
74
+ if derivatives == 0:
75
+ name = "L_" + str(lebesgue)
76
+ elif lebesgue == 2:
77
+ name = "H_" + str(derivatives)
78
+ else:
79
+ name = "W_" + str(derivatives) + ", " + str(lebesgue)
80
+
81
+ super(Sobolev, self).__init__(name, parents)
82
+
83
+ def __lt__(self, other):
84
+ """In common with intrinsic Python sets, < indicates "is a proper subset of"."""
85
+
86
+ if isinstance(other, Sobolev):
87
+ if self.lebesgue >= other.lebesgue:
88
+ return other.derivatives < self.derivatives or other in self.parents
89
+
90
+ return other in self.parents
91
+
92
+ def __call__(self, shape):
93
+ return Sobolev(self.derivatives, self.lebesgue, shape, name=self.name, parents=self.parents)
94
+
95
+
96
+ class Continuous(InterpolationSpace):
97
+ """
98
+ Describes the continuous space C_n
99
+
100
+ Arguments:
101
+ - Derivatives(n): the number of times the functions can be continously differentiated
102
+ """
103
+
104
+ def __init__(self, derivatives, shape=None, parents=[]):
105
+ self.derivatives = derivatives
106
+ self.shape = shape
107
+ name = "C_" + str(derivatives)
108
+ super(Continuous, self).__init__(name, parents)
109
+
110
+ def __lt__(self, other):
111
+ """In common with intrinsic Python sets, < indicates "is a proper subset of"."""
112
+
113
+ if isinstance(other, Continuous):
114
+ return other.derivatives > self.derivatives
115
+
116
+ return other in self.parents
117
+
118
+ def __call__(self, shape):
119
+ return Continuous(self.derivatives, shape=shape, parents=self.parents)
120
+
121
+
122
+ C0 = Continuous(0)
123
+ L2 = Sobolev(0, 2)
124
+ H1 = Sobolev(1, 2)
125
+ HDiv = Sobolev(0, 2, name="HDiv", parents=[L2])
126
+ HCurl = Sobolev(0, 2, name="HCurl", parents=[L2])
127
+
128
+ # Want to have
129
+ # c_0
130
+ # c_n
131
+ # w_m,p st mp < 1
132
+
133
+ # h_n ~ w_n,2
134
+ # l_2 ~ h_0
135
+ # h_1
136
+
137
+ # inclusions
138
+ # c_0
@@ -0,0 +1,203 @@
1
+ from FIAT.polynomial_set import ONPolynomialSet
2
+ from FIAT.quadrature_schemes import create_quadrature
3
+ from FIAT import expansions, polynomial_set, reference_element
4
+ from itertools import chain
5
+ from fuse.utils import tabulate_sympy, max_deg_sp_mat
6
+ import sympy as sp
7
+ import numpy as np
8
+
9
+
10
+ class PolynomialSpace(object):
11
+ """
12
+ contains: the degree of the maximum degree Lagrange space that is spanned by this element. If this
13
+ element's polynomial space does not include the constant function, this function should
14
+ return -1.
15
+
16
+ maxdegree: the degree of the minimum degree Lagrange space that spans this element.If this
17
+ element contains basis functions that are not in any Lagrange space, this property should
18
+ be None.
19
+
20
+ mindegree: the degree of the polynomial in the space with the lowest degree.
21
+
22
+ Note that on a simplex cells, the polynomial space of Lagrange space is a complete polynomial
23
+ space, but on other cells this is not true. For example, on quadrilateral cells, the degree 1
24
+ Lagrange space includes the degree 2 polynomial xy.
25
+ """
26
+
27
+ def __init__(self, maxdegree, contains=None, mindegree=0, set_shape=False):
28
+ self.maxdegree = maxdegree
29
+ self.mindegree = mindegree
30
+
31
+ if not contains and mindegree == 0:
32
+ self.contains = maxdegree
33
+ elif not contains and mindegree >= 0:
34
+ self.contains = -1
35
+ else:
36
+ self.contains = contains
37
+
38
+ self.set_shape = set_shape
39
+
40
+ def complete(self):
41
+ return self.mindegree == self.maxdegree
42
+
43
+ def degree(self):
44
+ return self.maxdegree
45
+
46
+ def to_ON_polynomial_set(self, ref_el, k=None):
47
+ # how does super/sub degrees work here
48
+ if not isinstance(ref_el, reference_element.Cell):
49
+ ref_el = ref_el.to_fiat()
50
+ sd = ref_el.get_spatial_dimension()
51
+ if self.set_shape:
52
+ shape = (sd,)
53
+ else:
54
+ shape = tuple()
55
+
56
+ if self.mindegree > 0:
57
+ base_ON = ONPolynomialSet(ref_el, self.maxdegree, shape, scale="orthonormal")
58
+ dimPmin = expansions.polynomial_dimension(ref_el, self.mindegree)
59
+ dimPmax = expansions.polynomial_dimension(ref_el, self.maxdegree)
60
+ if self.set_shape:
61
+ indices = list(chain(*(range(i * dimPmin, i * dimPmax) for i in range(sd))))
62
+ else:
63
+ indices = list(range(dimPmin, dimPmax))
64
+ restricted_ON = base_ON.take(indices)
65
+ return restricted_ON
66
+ return ONPolynomialSet(ref_el, self.maxdegree, shape, scale="orthonormal")
67
+
68
+ def __repr__(self):
69
+ res = ""
70
+ if self.complete():
71
+ res += "P" + str(self.maxdegree)
72
+ elif self.mindegree > 0:
73
+ res = "P" + "(min " + str(self.mindegree) + " max " + str(self.maxdegree) + ")"
74
+ else:
75
+ res += "Psub" + str(self.contains) + "sup" + str(self.maxdegree)
76
+ if self.set_shape:
77
+ res += "^d"
78
+ return res
79
+
80
+ def __mul__(self, x):
81
+ """
82
+ When multiplying a Polynomial Space by a sympy object, you need to multiply with
83
+ the sympy object on the right. This is due to Sympy's implementation of __mul__ not
84
+ passing to this handler as it should.
85
+ """
86
+ if isinstance(x, sp.Symbol):
87
+ return ConstructedPolynomialSpace([x], [self])
88
+ elif isinstance(x, sp.Matrix):
89
+ return ConstructedPolynomialSpace([x], [self])
90
+ else:
91
+ raise TypeError(f'Cannot multiply a PolySpace with {type(x)}')
92
+
93
+ __rmul__ = __mul__
94
+
95
+ def __add__(self, x):
96
+ return ConstructedPolynomialSpace([1, 1], [self, x])
97
+
98
+ def restrict(self, min_degree, max_degree):
99
+ return PolynomialSpace(max_degree, contains=-1, mindegree=min_degree, set_shape=self.set_shape)
100
+
101
+ def _to_dict(self):
102
+ return {"set_shape": self.set_shape, "min": self.mindegree, "contains": self.contains, "max": self.maxdegree}
103
+
104
+ def dict_id(self):
105
+ return "PolynomialSpace"
106
+
107
+ def _from_dict(obj_dict):
108
+ return PolynomialSpace(obj_dict["max"], obj_dict["contains"], obj_dict["min"], obj_dict["set_shape"])
109
+
110
+
111
+ class ConstructedPolynomialSpace(PolynomialSpace):
112
+ """
113
+ Sub degree is inherited from the largest of the component spaces,
114
+ super degree is unknown.
115
+
116
+ weights can either be 1 or a polynomial in x, where x in R^d
117
+ """
118
+ def __init__(self, weights, spaces):
119
+
120
+ self.weights = weights
121
+ self.spaces = spaces
122
+
123
+ maxdegree = max([space.maxdegree for space in spaces])
124
+ mindegree = min([space.mindegree for space in spaces])
125
+ vec = any([s.set_shape for s in spaces])
126
+
127
+ super(ConstructedPolynomialSpace, self).__init__(maxdegree, -1, mindegree, set_shape=vec)
128
+
129
+ def __repr__(self):
130
+ return "+".join([str(w) + "*" + str(x) for (w, x) in zip(self.weights, self.spaces)])
131
+
132
+ def to_ON_polynomial_set(self, ref_el):
133
+ if not isinstance(ref_el, reference_element.Cell):
134
+ ref_el = ref_el.to_fiat()
135
+ k = max([s.maxdegree for s in self.spaces])
136
+ space_poly_sets = [s.to_ON_polynomial_set(ref_el) for s in self.spaces]
137
+ sd = ref_el.get_spatial_dimension()
138
+
139
+ if all([w == 1 for w in self.weights]):
140
+ weighted_sets = space_poly_sets
141
+
142
+ # otherwise have to work on this through tabulation
143
+
144
+ Q = create_quadrature(ref_el, 2 * (k + 1))
145
+ Qpts, Qwts = Q.get_points(), Q.get_weights()
146
+ weighted_sets = []
147
+
148
+ for (space, w) in zip(space_poly_sets, self.weights):
149
+ if not (isinstance(w, sp.Expr) or isinstance(w, sp.Matrix)):
150
+ weighted_sets.append(space)
151
+ else:
152
+ w_deg = max_deg_sp_mat(w)
153
+ Pkpw = ONPolynomialSet(ref_el, space.degree + w_deg, scale="orthonormal")
154
+ vec_Pkpw = ONPolynomialSet(ref_el, space.degree + w_deg, (sd,), scale="orthonormal")
155
+
156
+ space_at_Qpts = space.tabulate(Qpts)[(0,) * sd]
157
+ Pkpw_at_Qpts = Pkpw.tabulate(Qpts)[(0,) * sd]
158
+
159
+ tabulated_expr = tabulate_sympy(w, Qpts).T
160
+ scaled_at_Qpts = space_at_Qpts[:, None, :] * tabulated_expr[None, :, :]
161
+ PkHw_coeffs = np.dot(np.multiply(scaled_at_Qpts, Qwts), Pkpw_at_Qpts.T)
162
+ weighted_sets.append(polynomial_set.PolynomialSet(ref_el,
163
+ space.degree + w_deg,
164
+ space.degree + w_deg,
165
+ vec_Pkpw.get_expansion_set(),
166
+ PkHw_coeffs))
167
+ combined_sets = weighted_sets[0]
168
+ for i in range(1, len(weighted_sets)):
169
+ combined_sets = polynomial_set.polynomial_set_union_normalized(combined_sets, weighted_sets[i])
170
+ return combined_sets
171
+
172
+ def __mul__(self, x):
173
+ return ConstructedPolynomialSpace([x*w for w in self.weights],
174
+ self.spaces)
175
+ __rmul__ = __mul__
176
+
177
+ def __add__(self, x):
178
+ return ConstructedPolynomialSpace(self.weights.extend([1]),
179
+ self.spaces.extend(x))
180
+
181
+ def _to_dict(self):
182
+ super_dict = super(ConstructedPolynomialSpace, self)._to_dict()
183
+ super_dict["spaces"] = self.spaces
184
+ super_dict["weights"] = self.weights
185
+ return super_dict
186
+
187
+ def dict_id(self):
188
+ return "ConstructedPolynomialSpace"
189
+
190
+ def _from_dict(obj_dict):
191
+ return ConstructedPolynomialSpace(obj_dict["weights"], obj_dict["spaces"])
192
+
193
+
194
+ P0 = PolynomialSpace(0)
195
+ P1 = PolynomialSpace(1)
196
+ P2 = PolynomialSpace(2)
197
+ P3 = PolynomialSpace(3)
198
+ P4 = PolynomialSpace(4)
199
+
200
+ Q1 = PolynomialSpace(1, 2)
201
+ Q2 = PolynomialSpace(2, 3)
202
+ Q3 = PolynomialSpace(3, 4)
203
+ Q4 = PolynomialSpace(4, 5)
fuse/traces.py ADDED
@@ -0,0 +1,196 @@
1
+ import numpy as np
2
+ import sympy as sp
3
+ import matplotlib.pyplot as plt
4
+ from fuse.utils import sympy_to_numpy
5
+
6
+
7
+ class Trace():
8
+
9
+ def __init__(self, cell):
10
+ self.domain = cell
11
+
12
+ def __call__(self, trace_entity, g):
13
+ raise NotImplementedError("Trace uninstanitated")
14
+
15
+ def plot(self, ax, coord, trace_entity, g, **kwargs):
16
+ raise NotImplementedError("Trace uninstanitated")
17
+
18
+ def tabulate(self, Qpts, trace_entity, g):
19
+ raise NotImplementedError("Tabulation uninstantiated")
20
+
21
+ def _to_dict(self):
22
+ return {"trace": str(self)}
23
+
24
+ def dict_id(self):
25
+ return "Trace"
26
+
27
+ def _from_dict(obj_dict):
28
+ # might want to actually save these as functions or something for ambiguity?
29
+ tr_id = obj_dict["trace"]
30
+ if tr_id == "H1":
31
+ return TrH1
32
+ elif tr_id == "HDiv":
33
+ return TrHDiv
34
+ elif tr_id == "HCurl":
35
+ return TrHCurl
36
+ elif tr_id == "Grad":
37
+ return TrGrad
38
+ elif tr_id == "Hess":
39
+ return TrHess
40
+ raise ValueError("Trace not found")
41
+
42
+
43
+ class TrH1(Trace):
44
+
45
+ def __init__(self, cell):
46
+ super(TrH1, self).__init__(cell)
47
+
48
+ def __call__(self, v, trace_entity, g):
49
+ return v
50
+
51
+ def plot(self, ax, coord, trace_entity, g, **kwargs):
52
+ ax.scatter(*coord, **kwargs)
53
+
54
+ def tabulate(self, Qpts, trace_entity, g):
55
+ return np.ones_like(Qpts)
56
+
57
+ def __repr__(self):
58
+ return "H1"
59
+
60
+
61
+ class TrHDiv(Trace):
62
+
63
+ def __init__(self, cell):
64
+ super(TrHDiv, self).__init__(cell)
65
+
66
+ def __call__(self, v, trace_entity, g):
67
+ def apply(*x):
68
+ result = np.dot(self.tabulate(None, trace_entity, g), np.array(v(*x)).squeeze())
69
+ if isinstance(result, np.float64):
70
+ # todo: might always be a float
71
+ return (result,)
72
+ return tuple(result)
73
+ return apply
74
+
75
+ def plot(self, ax, coord, trace_entity, g, **kwargs):
76
+ # plot dofs of the type associated with this space
77
+ permuted = self.domain.permute_entities(g, trace_entity.dimension)
78
+ orientation = [o for (ent, o) in permuted if ent == trace_entity.id][0]
79
+
80
+ entityBasis = np.array(trace_entity.orient(orientation).basis_vectors())
81
+ cellEntityBasis = np.array(self.domain.basis_vectors(entity=trace_entity))
82
+ basis = np.matmul(entityBasis, cellEntityBasis)
83
+ if len(coord) == 2:
84
+ vec = np.matmul(np.array([[0, 1], [-1, 0]]), basis.T)
85
+ else:
86
+ vec = np.cross(basis[0], basis[1])
87
+ ax.quiver(*coord, *vec, **kwargs)
88
+
89
+ def tabulate(self, Qpts, trace_entity, g):
90
+ entityBasis = np.array(trace_entity.basis_vectors())
91
+ cellEntityBasis = np.array(self.domain.basis_vectors(entity=trace_entity))
92
+ basis = np.matmul(entityBasis, cellEntityBasis)
93
+
94
+ if trace_entity.dimension == 1:
95
+ result = np.matmul(basis, np.array([[0, -1], [1, 0]]))
96
+ elif trace_entity.dimension == 2:
97
+ result = np.cross(basis[0], basis[1])
98
+ else:
99
+ raise ValueError("Immersion of HDiv edges not defined in 3D")
100
+
101
+ return result
102
+
103
+ def __repr__(self):
104
+ return "HDiv"
105
+
106
+
107
+ class TrHCurl(Trace):
108
+
109
+ def __init__(self, cell):
110
+ super(TrHCurl, self).__init__(cell)
111
+
112
+ def __call__(self, v, trace_entity, g):
113
+ def apply(*x):
114
+ result = np.dot(self.tabulate(None, trace_entity, g), np.array(v(*x)).squeeze())
115
+ if isinstance(result, np.float64):
116
+ return (result,)
117
+ return tuple(result)
118
+ return apply
119
+
120
+ def tabulate(self, Qpts, trace_entity, g):
121
+ tangent = np.array(trace_entity.basis_vectors())
122
+ subEntityBasis = np.array(self.domain.basis_vectors(entity=trace_entity))
123
+ result = np.matmul(tangent, subEntityBasis)
124
+ return result
125
+
126
+ def plot(self, ax, coord, trace_entity, g, **kwargs):
127
+ permuted = self.domain.permute_entities(g, trace_entity.dimension)
128
+ orientation = [o for (ent, o) in permuted if ent == trace_entity.id][0]
129
+
130
+ tangent = np.array(trace_entity.orient(orientation).basis_vectors())
131
+ subEntityBasis = np.array(self.domain.basis_vectors(entity=trace_entity))
132
+ vec = np.matmul(tangent, subEntityBasis)[0]
133
+ ax.quiver(*coord, *vec, **kwargs)
134
+
135
+ def __repr__(self):
136
+ return "HCurl"
137
+
138
+
139
+ class TrGrad(Trace):
140
+
141
+ def __init__(self, cell):
142
+ super(TrGrad, self).__init__(cell)
143
+
144
+ def __call__(self, v, trace_entity, g):
145
+ # Compute grad v and then dot with tangent rotated according to the group member
146
+ tangent = np.array(g(np.array(self.domain.basis_vectors())[0]))
147
+
148
+ def apply(*x):
149
+ X = sp.DeferredVector('x')
150
+ dX = tuple([X[i] for i in range(self.domain.dim())])
151
+ compute_v = v(*dX, sym=True)
152
+ grad_v = sp.Matrix([sp.diff(compute_v, dX[i]) for i in range(len(dX))])
153
+ eval_grad_v = sympy_to_numpy(grad_v, dX, v.attach_func(*x))
154
+ result = np.dot(tangent, np.array(eval_grad_v))
155
+
156
+ if not hasattr(result, "__iter__"):
157
+ return (result,)
158
+ return tuple(result)
159
+ return apply
160
+
161
+ def plot(self, ax, coord, trace_entity, g, **kwargs):
162
+ circle1 = plt.Circle(coord, 0.075, fill=False, **kwargs)
163
+ ax.add_patch(circle1)
164
+
165
+ def __repr__(self):
166
+ return "Grad"
167
+
168
+
169
+ class TrHess(Trace):
170
+
171
+ def __init__(self, cell):
172
+ super(TrHess, self).__init__(cell)
173
+
174
+ def __call__(self, v, trace_entity, g):
175
+ b0, b1 = self.domain.basis_vectors()
176
+ tangent0 = np.array(g(b0))
177
+ tangent1 = np.array(g(b1))
178
+
179
+ def apply(*x):
180
+ X = sp.DeferredVector('x')
181
+
182
+ dX = tuple([X[i] for i in range(self.domain.dim())])
183
+ hess_v = sp.Matrix([[sp.diff(v(*dX, sym=True), dX[i], dX[j]) for i in range(len(dX))] for j in range(len(dX))])
184
+ eval_hess_v = sympy_to_numpy(hess_v, dX, v.attach_func(*x))
185
+ result = np.dot(np.matmul(tangent0, np.array(eval_hess_v)), tangent1)
186
+ if not hasattr(result, "__iter__"):
187
+ return (result,)
188
+ return tuple(result)
189
+ return apply
190
+
191
+ def plot(self, ax, coord, trace_entity, g, **kwargs):
192
+ circle1 = plt.Circle(coord, 0.15, fill=False, **kwargs)
193
+ ax.add_patch(circle1)
194
+
195
+ def __repr__(self):
196
+ return "Hess"