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/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"