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.
- fuse/__init__.py +10 -0
- fuse/cells.py +850 -0
- fuse/dof.py +363 -0
- fuse/groups.py +388 -0
- fuse/serialisation.py +121 -0
- fuse/spaces/__init__.py +0 -0
- fuse/spaces/element_sobolev_spaces.py +80 -0
- fuse/spaces/interpolation_spaces.py +138 -0
- fuse/spaces/polynomial_spaces.py +203 -0
- fuse/traces.py +196 -0
- fuse/triples.py +470 -0
- fuse/utils.py +64 -0
- fuse_element-0.1.dev0.dist-info/LICENSE +165 -0
- fuse_element-0.1.dev0.dist-info/METADATA +17 -0
- fuse_element-0.1.dev0.dist-info/RECORD +17 -0
- fuse_element-0.1.dev0.dist-info/WHEEL +5 -0
- fuse_element-0.1.dev0.dist-info/top_level.txt +1 -0
fuse/dof.py
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
from FIAT.quadrature_schemes import create_quadrature
|
|
2
|
+
from FIAT.quadrature import FacetQuadratureRule
|
|
3
|
+
from FIAT.functional import PointEvaluation, FrobeniusIntegralMoment
|
|
4
|
+
from fuse.utils import sympy_to_numpy
|
|
5
|
+
import numpy as np
|
|
6
|
+
import sympy as sp
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Pairing():
|
|
10
|
+
"""
|
|
11
|
+
Akin to an inner product, the pairing combines a kernel and an input function
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self):
|
|
15
|
+
self.entity = None
|
|
16
|
+
|
|
17
|
+
def _to_dict(self):
|
|
18
|
+
o_dict = {"entity": self.entity}
|
|
19
|
+
return o_dict
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class DeltaPairing(Pairing):
|
|
23
|
+
"""
|
|
24
|
+
The delta pairing allows the evaluation at a single points
|
|
25
|
+
|
|
26
|
+
Calling method:
|
|
27
|
+
:param: kernel: Normally a PointKernel
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self):
|
|
31
|
+
super(DeltaPairing, self).__init__()
|
|
32
|
+
|
|
33
|
+
def __call__(self, kernel, v, cell):
|
|
34
|
+
assert isinstance(kernel, PointKernel)
|
|
35
|
+
return v(*kernel.pt)
|
|
36
|
+
|
|
37
|
+
def convert_to_fiat(self, ref_el, dof, interpolant_deg):
|
|
38
|
+
pt = dof.eval(MyTestFunction(lambda *x: x))
|
|
39
|
+
return PointEvaluation(ref_el, pt)
|
|
40
|
+
|
|
41
|
+
def add_entity(self, entity):
|
|
42
|
+
res = DeltaPairing()
|
|
43
|
+
res.entity = entity
|
|
44
|
+
return res
|
|
45
|
+
|
|
46
|
+
def __repr__(self):
|
|
47
|
+
return "{fn}({kernel})"
|
|
48
|
+
|
|
49
|
+
def dict_id(self):
|
|
50
|
+
return "Delta"
|
|
51
|
+
|
|
52
|
+
def _from_dict(obj_dict):
|
|
53
|
+
new_obj = DeltaPairing()
|
|
54
|
+
new_obj.add_entity(obj_dict["entity"])
|
|
55
|
+
return new_obj
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class L2Pairing(Pairing):
|
|
59
|
+
""" need to think about the abstraction level here -
|
|
60
|
+
are we wanting to define them as quadrature now? or defer this?
|
|
61
|
+
"""
|
|
62
|
+
def __init__(self):
|
|
63
|
+
super(L2Pairing, self).__init__()
|
|
64
|
+
|
|
65
|
+
def __call__(self, kernel, v, cell):
|
|
66
|
+
# print(self.entity)
|
|
67
|
+
# if cell == self.entity:
|
|
68
|
+
# # print("evaluating", kernel, v, "on", self.entity)
|
|
69
|
+
# quadrature = create_quadrature(self.entity.to_fiat(), 5)
|
|
70
|
+
# # need quadrature here too - therefore need the information from the triple.
|
|
71
|
+
# else:
|
|
72
|
+
# ref_el = cell.to_fiat()
|
|
73
|
+
# print(cell)
|
|
74
|
+
# ent_id = self.entity.id - ref_el.fe_cell.get_starter_ids()[self.entity.dim()]
|
|
75
|
+
# entity = ref_el.construct_subelement(self.entity.dim())
|
|
76
|
+
# Q_ref = create_quadrature(entity, 5)
|
|
77
|
+
# quadrature = FacetQuadratureRule(ref_el, self.entity.dim(), ent_id, Q_ref)
|
|
78
|
+
quadrature = create_quadrature(self.entity.to_fiat(), 5)
|
|
79
|
+
|
|
80
|
+
def kernel_dot(x):
|
|
81
|
+
return np.dot(kernel(*x), v(*x))
|
|
82
|
+
|
|
83
|
+
return quadrature.integrate(kernel_dot)
|
|
84
|
+
|
|
85
|
+
def tabulate(self):
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
def add_entity(self, entity):
|
|
89
|
+
res = L2Pairing()
|
|
90
|
+
res.entity = entity
|
|
91
|
+
return res
|
|
92
|
+
|
|
93
|
+
def convert_to_fiat(self, ref_el, dof, interpolant_degree):
|
|
94
|
+
total_deg = interpolant_degree + dof.kernel.degree()
|
|
95
|
+
ent_id = self.entity.id - ref_el.fe_cell.get_starter_ids()[self.entity.dim()]
|
|
96
|
+
entity = ref_el.construct_subelement(self.entity.dim())
|
|
97
|
+
Q_ref = create_quadrature(entity, total_deg)
|
|
98
|
+
Q = FacetQuadratureRule(ref_el, self.entity.dim(), ent_id, Q_ref)
|
|
99
|
+
Jdet = Q.jacobian_determinant()
|
|
100
|
+
qpts, _ = Q.get_points(), Q.get_weights()
|
|
101
|
+
print(qpts)
|
|
102
|
+
print(dof.tabulate(qpts))
|
|
103
|
+
f_at_qpts = dof.tabulate(qpts).T / Jdet
|
|
104
|
+
print(len(Q.pts))
|
|
105
|
+
print(f_at_qpts.shape)
|
|
106
|
+
functional = FrobeniusIntegralMoment(ref_el, Q, f_at_qpts)
|
|
107
|
+
return functional
|
|
108
|
+
|
|
109
|
+
def __repr__(self):
|
|
110
|
+
return "integral_{}({{kernel}} * {{fn}}) dx)".format(str(self.entity))
|
|
111
|
+
|
|
112
|
+
def dict_id(self):
|
|
113
|
+
return "L2Inner"
|
|
114
|
+
|
|
115
|
+
def _from_dict(obj_dict):
|
|
116
|
+
new_obj = L2Pairing()
|
|
117
|
+
new_obj.add_entity(obj_dict["entity"])
|
|
118
|
+
return new_obj
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class BaseKernel():
|
|
122
|
+
|
|
123
|
+
def __init__(self):
|
|
124
|
+
self.attachment = False
|
|
125
|
+
|
|
126
|
+
def permute(self, g):
|
|
127
|
+
raise NotImplementedError("This method should be implemented by the subclass")
|
|
128
|
+
|
|
129
|
+
def __repr__(self):
|
|
130
|
+
return "BaseKernel"
|
|
131
|
+
|
|
132
|
+
def __call__(self, *args):
|
|
133
|
+
raise NotImplementedError("This method should be implemented by the subclass")
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class PointKernel(BaseKernel):
|
|
137
|
+
|
|
138
|
+
def __init__(self, x):
|
|
139
|
+
if not isinstance(x, tuple):
|
|
140
|
+
x = (x,)
|
|
141
|
+
self.pt = x
|
|
142
|
+
super(PointKernel, self).__init__()
|
|
143
|
+
|
|
144
|
+
def __repr__(self):
|
|
145
|
+
x = list(map(str, list(self.pt)))
|
|
146
|
+
return ','.join(x)
|
|
147
|
+
|
|
148
|
+
def degree(self):
|
|
149
|
+
return 1
|
|
150
|
+
|
|
151
|
+
def permute(self, g):
|
|
152
|
+
return PointKernel(g(self.pt))
|
|
153
|
+
|
|
154
|
+
def __call__(self, *args):
|
|
155
|
+
return self.pt
|
|
156
|
+
|
|
157
|
+
def tabulate(self, Qpts):
|
|
158
|
+
return np.array([self.pt for _ in Qpts]).astype(np.float64)
|
|
159
|
+
|
|
160
|
+
def _to_dict(self):
|
|
161
|
+
o_dict = {"pt": self.pt}
|
|
162
|
+
return o_dict
|
|
163
|
+
|
|
164
|
+
def dict_id(self):
|
|
165
|
+
return "PointKernel"
|
|
166
|
+
|
|
167
|
+
def _from_dict(obj_dict):
|
|
168
|
+
return PointKernel(tuple(obj_dict["pt"]))
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class PolynomialKernel(BaseKernel):
|
|
172
|
+
|
|
173
|
+
def __init__(self, fn, symbols=[]):
|
|
174
|
+
if len(symbols) != 0 and not sp.sympify(fn).as_poly():
|
|
175
|
+
raise ValueError("Function argument must be able to be interpreted as a sympy polynomial")
|
|
176
|
+
self.fn = sp.sympify(fn)
|
|
177
|
+
self.syms = symbols
|
|
178
|
+
super(PolynomialKernel, self).__init__()
|
|
179
|
+
|
|
180
|
+
def __repr__(self):
|
|
181
|
+
return str(self.fn)
|
|
182
|
+
|
|
183
|
+
def degree(self):
|
|
184
|
+
if len(self.fn.free_symbols) == 0:
|
|
185
|
+
return 1
|
|
186
|
+
return self.fn.as_poly().total_degree()
|
|
187
|
+
|
|
188
|
+
def permute(self, g):
|
|
189
|
+
new_fn = self.fn.subs({self.syms[i]: g(self.syms)[i] for i in range(len(self.syms))})
|
|
190
|
+
return PolynomialKernel(new_fn, symbols=self.syms)
|
|
191
|
+
|
|
192
|
+
def __call__(self, *args):
|
|
193
|
+
res = sympy_to_numpy(self.fn, self.syms, args[:len(self.syms)])
|
|
194
|
+
if not hasattr(res, '__iter__'):
|
|
195
|
+
return [res]
|
|
196
|
+
return res
|
|
197
|
+
|
|
198
|
+
def tabulate(self, Qpts):
|
|
199
|
+
return np.array([self(*pt) for pt in Qpts]).astype(np.float64)
|
|
200
|
+
|
|
201
|
+
def _to_dict(self):
|
|
202
|
+
o_dict = {"fn": self.fn}
|
|
203
|
+
return o_dict
|
|
204
|
+
|
|
205
|
+
def dict_id(self):
|
|
206
|
+
return "PolynomialKernel"
|
|
207
|
+
|
|
208
|
+
def _from_dict(obj_dict):
|
|
209
|
+
return PolynomialKernel(obj_dict["fn"])
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class DOF():
|
|
213
|
+
|
|
214
|
+
def __init__(self, pairing, kernel, entity=None, attachment=None, target_space=None, g=None, immersed=False, generation=None, sub_id=None, cell=None):
|
|
215
|
+
self.pairing = pairing
|
|
216
|
+
self.kernel = kernel
|
|
217
|
+
self.immersed = immersed
|
|
218
|
+
self.trace_entity = entity
|
|
219
|
+
self.attachment = attachment
|
|
220
|
+
self.target_space = target_space
|
|
221
|
+
self.g = g
|
|
222
|
+
self.id = None
|
|
223
|
+
self.sub_id = sub_id
|
|
224
|
+
self.cell = cell
|
|
225
|
+
|
|
226
|
+
if generation is None:
|
|
227
|
+
self.generation = {}
|
|
228
|
+
else:
|
|
229
|
+
self.generation = generation
|
|
230
|
+
if entity is not None:
|
|
231
|
+
self.pairing = self.pairing.add_entity(entity)
|
|
232
|
+
|
|
233
|
+
def __call__(self, g):
|
|
234
|
+
new_generation = self.generation.copy()
|
|
235
|
+
return DOF(self.pairing, self.kernel.permute(g), self.trace_entity, self.attachment, self.target_space, g, self.immersed, new_generation, self.sub_id, self.cell)
|
|
236
|
+
|
|
237
|
+
def eval(self, fn, pullback=True):
|
|
238
|
+
return self.pairing(self.kernel, fn, self.cell)
|
|
239
|
+
|
|
240
|
+
def tabulate(self, Qpts):
|
|
241
|
+
return self.kernel.tabulate(Qpts)
|
|
242
|
+
|
|
243
|
+
def add_context(self, dof_gen, cell, space, g, overall_id=None, generator_id=None):
|
|
244
|
+
# For some of these, we only want to store the first instance of each
|
|
245
|
+
self.generation[cell.dim()] = dof_gen
|
|
246
|
+
self.cell = cell
|
|
247
|
+
if self.trace_entity is None:
|
|
248
|
+
self.trace_entity = cell
|
|
249
|
+
self.pairing = self.pairing.add_entity(cell)
|
|
250
|
+
if self.target_space is None:
|
|
251
|
+
self.target_space = space
|
|
252
|
+
if self.id is None and overall_id is not None:
|
|
253
|
+
self.id = overall_id
|
|
254
|
+
if self.sub_id is None and generator_id is not None:
|
|
255
|
+
self.sub_id = generator_id
|
|
256
|
+
|
|
257
|
+
def convert_to_fiat(self, ref_el, interpolant_degree):
|
|
258
|
+
return self.pairing.convert_to_fiat(ref_el, self, interpolant_degree)
|
|
259
|
+
raise NotImplementedError("Fiat conversion only implemented for Point eval")
|
|
260
|
+
|
|
261
|
+
def __repr__(self, fn="v"):
|
|
262
|
+
return str(self.pairing).format(fn=fn, kernel=self.kernel)
|
|
263
|
+
|
|
264
|
+
def immerse(self, entity, attachment, target_space, g, triple):
|
|
265
|
+
new_generation = self.generation.copy()
|
|
266
|
+
return ImmersedDOF(self.pairing, self.kernel, entity, attachment, target_space, g, triple, new_generation, self.sub_id, self.cell)
|
|
267
|
+
|
|
268
|
+
def _to_dict(self):
|
|
269
|
+
""" almost certainly needs more things"""
|
|
270
|
+
o_dict = {"pairing": self.pairing, "kernel": self.kernel}
|
|
271
|
+
return o_dict
|
|
272
|
+
|
|
273
|
+
def dict_id(self):
|
|
274
|
+
return "DOF"
|
|
275
|
+
|
|
276
|
+
def _from_dict(obj_dict):
|
|
277
|
+
return DOF(obj_dict["pairing"], obj_dict["kernel"])
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
class ImmersedDOF(DOF):
|
|
281
|
+
# probably need to add a convert to fiat method here to capture derivatives from immersion
|
|
282
|
+
def __init__(self, pairing, kernel, entity=None, attachment=None, target_space=None, g=None, triple=None, generation=None, sub_id=None, cell=None):
|
|
283
|
+
self.immersed = True
|
|
284
|
+
self.triple = triple
|
|
285
|
+
super(ImmersedDOF, self).__init__(pairing, kernel, entity=entity, attachment=attachment, target_space=target_space, g=g, immersed=True, generation=generation, sub_id=sub_id, cell=cell)
|
|
286
|
+
|
|
287
|
+
def eval(self, fn, pullback=True):
|
|
288
|
+
attached_fn = fn.attach(self.attachment)
|
|
289
|
+
|
|
290
|
+
if pullback:
|
|
291
|
+
attached_fn = self.target_space(attached_fn, self.trace_entity, self.g)
|
|
292
|
+
|
|
293
|
+
return self.pairing(self.kernel, attached_fn, self.cell)
|
|
294
|
+
|
|
295
|
+
def tabulate(self, Qpts):
|
|
296
|
+
immersion = self.target_space.tabulate(Qpts, self.trace_entity, self.g)
|
|
297
|
+
res = self.kernel.tabulate(Qpts)
|
|
298
|
+
return immersion*res
|
|
299
|
+
|
|
300
|
+
def __call__(self, g):
|
|
301
|
+
permuted = self.cell.permute_entities(g, self.trace_entity.dim())
|
|
302
|
+
index_trace = self.cell.d_entities_ids(self.trace_entity.dim()).index(self.trace_entity.id)
|
|
303
|
+
new_trace_entity = self.cell.get_node(permuted[index_trace][0]).orient(permuted[index_trace][1])
|
|
304
|
+
|
|
305
|
+
return ImmersedDOF(self.pairing, self.kernel.permute(permuted[index_trace][1]), new_trace_entity,
|
|
306
|
+
self.attachment, self.target_space, g, self.triple, self.generation, self.sub_id, self.cell)
|
|
307
|
+
|
|
308
|
+
def __repr__(self):
|
|
309
|
+
fn = "tr_{1}_{0}(v)".format(str(self.trace_entity), str(self.target_space))
|
|
310
|
+
return super(ImmersedDOF, self).__repr__(fn)
|
|
311
|
+
|
|
312
|
+
def immerse(self, entity, attachment, trace, g):
|
|
313
|
+
raise RuntimeError("Error: Immersing twice not supported")
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
class MyTestFunction():
|
|
317
|
+
|
|
318
|
+
def __init__(self, eq, attach_func=None, symbols=None):
|
|
319
|
+
self.eq = eq
|
|
320
|
+
self.attach_func = attach_func
|
|
321
|
+
self.symbols = symbols
|
|
322
|
+
|
|
323
|
+
def __call__(self, *x, sym=False):
|
|
324
|
+
if self.symbols:
|
|
325
|
+
if self.attach_func and not sym:
|
|
326
|
+
res = self.eq.subs({symb: val for (symb, val) in zip(self.symbols, self.attach_func(*x))})
|
|
327
|
+
else:
|
|
328
|
+
res = self.eq.subs({symb: val for (symb, val) in zip(self.symbols, x)})
|
|
329
|
+
if res.free_symbols == set():
|
|
330
|
+
array = np.array(res).astype(np.float64)
|
|
331
|
+
return array
|
|
332
|
+
else:
|
|
333
|
+
return res
|
|
334
|
+
if self.attach_func and not sym:
|
|
335
|
+
return self.eq(*self.attach_func(*x))
|
|
336
|
+
else:
|
|
337
|
+
# TODO remove this as will already be symbolic
|
|
338
|
+
return self.eq(*x)
|
|
339
|
+
|
|
340
|
+
def attach(self, attachment):
|
|
341
|
+
if not self.attach_func:
|
|
342
|
+
return MyTestFunction(self.eq, attach_func=attachment, symbols=self.symbols)
|
|
343
|
+
else:
|
|
344
|
+
old_attach = self.attach_func
|
|
345
|
+
if self.symbols:
|
|
346
|
+
return MyTestFunction(self.eq,
|
|
347
|
+
attach_func=attachment(old_attach(*self.symbols)),
|
|
348
|
+
symbols=self.symbols)
|
|
349
|
+
else:
|
|
350
|
+
return MyTestFunction(self.eq,
|
|
351
|
+
attach_func=lambda *x: attachment(old_attach(*x)))
|
|
352
|
+
|
|
353
|
+
def __repr__(self):
|
|
354
|
+
if self.attach_func:
|
|
355
|
+
return "v(G(x))"
|
|
356
|
+
else:
|
|
357
|
+
return "v(x)"
|
|
358
|
+
|
|
359
|
+
def _to_dict(self):
|
|
360
|
+
return {"eq": self.eq}
|
|
361
|
+
|
|
362
|
+
def dict_id(self):
|
|
363
|
+
return "Function"
|