def test_indexing(): "Basic test for array indexing" sim = openmodes.Simulation() mesh = sim.load_mesh(osp.join(openmodes.geometry_dir, "SRR.geo")) group1 = sim.place_part() group2 = sim.place_part() srr1 = sim.place_part(mesh, parent=group1) srr2 = sim.place_part(mesh, parent=group1) srr3 = sim.place_part(mesh, parent=group2) basis = sim.basis_container[srr1] basis_len = len(basis) A = LookupArray( ((group1, sim.basis_container), (group2, sim.basis_container), 5, 3)) assert (A.shape == (2 * basis_len, basis_len, 5, 3)) A[group1, srr3] = 22.5 assert (np.all(A[srr1, :] == 22.5)) assert (np.all(A[srr2] == 22.5)) V = LookupArray((("E", "H"), (sim.parts, sim.basis_container)), dtype=np.complex128) V["E", group1] = -4.7 + 22j V["H", srr1] = 5.2 V["H", srr2] = 6.7 assert (np.all(V["E", group1] == V["E"][group1])) assert (np.all(V["E", srr1] == -4.7 + 22j)) assert (np.all(V["E", srr2].imag == 22))
def s(self): res = LookupArray( (('modes', ), (self.parent_part, self.macro_container)), dtype=np.complex128) for part in self.parts: res[:, part] = self.modes_of_parts[part.unique_id]['s'] return res
def val(self): "The value of the impedance matrix" Z = LookupArray( (self.sources, (self.part_o, self.basis_container), self.unknowns, (self.part_s, self.basis_container)), dtype=np.complex128) Z.simple_view()[:] = self.matrices['Z'] return Z
def __init__(self, part_o, part_s, basis_container, sources, unknowns, metadata=None, matrices=None, derivatives=None): self.md = metadata or dict() self.part_o = part_o self.part_s = part_s self.basis_container = basis_container self.sources = sources self.unknowns = unknowns # Note that the internal LookupArray format is different from the # final format as it excludes the quantity lookup. self.matrices = { name: LookupArray( ((part_o, basis_container), (part_s, basis_container)), dtype=np.complex128) for name in self.matrix_names } if matrices is not None: # fill out any matrices which are supplied for name, mat in list(matrices.items()): self.matrices[name][:] = mat # create the frequency derivatives of the matrices if derivatives is None: self.der = { name: LookupArray((( part_o, basis_container, ), (part_s, basis_container)), dtype=np.complex128) for name in self.matrix_names } else: self.der = derivatives
def source_vector(self, source_field, s, parent, extinction_field): "Calculate the relevant source vector for this operator" V = LookupArray((("E"), (parent, self.basis_container)), dtype=np.complex128) for part in parent.iter_single(): V["E", part] = self.source_single_part(source_field, s, part, extinction_field) return V
def gram_matrix(self, part): """Create a Gram matrix as a LookupArray""" G = self.basis_container[part].gram_matrix Gp = LookupArray( (self.unknowns, (part, self.basis_container), self.sources, (part, self.basis_container)), dtype=G.dtype) Gp[:] = 0.0 for unknown, source in zip(self.unknowns, self.sources): Gp[unknown, :, source, :] = G return Gp
def val(self): "The value of the impedance matrix" s = self.md['s'] alpha = self.md['alpha'] Z = LookupArray( (self.sources, (self.part_o, self.basis_container), self.unknowns, (self.part_s, self.basis_container)), dtype=np.complex128) Z.simple_view()[:] = ( alpha * (self.matrices['S'] / s + s * self.matrices['L']) + (1.0 - alpha) * self.matrices['M']) return Z
def source_vector(self, source_field, s, parent, extinction_field=False): "Calculate the relevant source vector for this operator" if extinction_field: return super(CfieOperator, self).source_vector(source_field, s, parent, True) fields = ("E", "nxH") V = LookupArray((fields, (parent, self.basis_container)), dtype=np.complex128) # define the functions to interpolate over the mesh def elec_func(r): return source_field.electric_field(s, r) def mag_func(r): return source_field.magnetic_field(s, r) for field in fields: if field == "E": field_func = elec_func source_cross = False elif field == "nxH": field_func = mag_func source_cross = True for part in parent.iter_single(): basis = self.basis_container[part] V[field, part] = basis.weight_function(field_func, self.integration_rule, part.nodes, source_cross) V_final = LookupArray((self.sources, (parent, self.basis_container)), dtype=np.complex128) V_final[:] = self.alpha * V["E"] + (1.0 - self.alpha) * V["nxH"] return V_final
def vr(self): "The right eigenvectors" res = LookupArray( (self.operator.unknowns, (self.parent_part, self.orig_container), ('modes', ), (self.parent_part, self.macro_container)), dtype=np.complex128) res[:] = 0.0 for part in self.parts: res[:, part, :, part] = self.modes_of_parts[part.unique_id]['vr'].reshape( res[:, part, :, part].shape) return res
def test_list_indexing(): "List mucks up LookupArrays, which is now warned about" sim = openmodes.Simulation() mesh = sim.load_mesh(osp.join(openmodes.geometry_dir, "SRR.geo")) srr1 = sim.place_part(mesh) srr2 = sim.place_part(mesh) res = LookupArray((sim.operator.unknowns, (sim.parts, sim.basis_container), ('modes', ), (srr2, sim.basis_container)), dtype=np.complex128) with pytest.warns(UserWarning): res[0, :, 0, [2]]
def solve(self, vec): """Solve the impedance matrix for a source vector. Caches the factorised matrix for efficiently solving multiple vectors""" if self.part_o != self.part_s: raise ValueError("Can only invert a self-impedance matrix") Z_lu = self.factored() if isinstance(vec, LookupArray): vec = vec.simple_view() lookup = (self.unknowns, (self.part_s, self.basis_container)) if len(vec.shape) > 1: lookup = lookup + (vec.shape[1], ) I = LookupArray(lookup, dtype=np.complex128) I_simp = I.simple_view() I_simp[:] = la.lu_solve(Z_lu, vec) return I
def empty_array(self, part=None, extra_dims=()): """ Create an empty array of the appropriate size to contain solutions for all of the parts, or a single part and its sub-parts Parameters ---------- part : Part, optional The part for which to create the vector. If not specified, all parts in the full simulation extra_dims : tuple, optional This can give the sizes of additional dimensions """ part = part or self.parts return LookupArray((self.operator.unknowns, (part, self.basis_container)) + extra_dims, dtype=np.complex128)
def val(self): "The value of the impedance matrix" s = self.md['s'] D_i = s * self.matrices['L_i'] + self.matrices['S_i'] / s D_o = s * self.matrices['L_o'] + self.matrices['S_o'] / s K_i = self.matrices['K_i'] K_o = self.matrices['K_o'] eta_o = self.md['eta_o'] eta_i = self.md['eta_i'] w_EFIE_i = self.md['w_EFIE_i'] w_EFIE_o = self.md['w_EFIE_o'] w_MFIE_i = self.md['w_MFIE_i'] w_MFIE_o = self.md['w_MFIE_o'] Z = LookupArray((("E", "H"), (self.part_o, self.basis_container), ("J", "M"), (self.part_s, self.basis_container)), dtype=np.complex128) # first calculate the external problem contributions Z["E", :, "J"] = eta_o * D_o * w_EFIE_o Z["E", :, "M"] = -K_o * w_EFIE_o Z["H", :, "J"] = K_o * w_MFIE_o Z["H", :, "M"] = D_o / eta_o * w_MFIE_o # The internal contributions are only for self-terms for part_o in self.part_o.iter_single(): for part_s in self.part_s.iter_single(): if part_o == part_s: Z["E", :, "J"][part_o, part_s] += eta_i[part_s] * D_i[ part_o, part_s] * w_EFIE_i[part_s] Z["E", :, "M"][part_o, part_s] -= K_i[part_o, part_s] * w_EFIE_i[part_s] Z["H", :, "J"][part_o, part_s] += K_i[part_o, part_s] * w_MFIE_i[part_s] Z["H", :, "M"][part_o, part_s] += D_i[ part_o, part_s] / eta_i[part_s] * w_MFIE_i[part_s] return Z
def source_vector(self, source_field, s, parent, extinction_field=False): "Calculate the relevant source vector for this operator" if extinction_field: fields = self.extinction_fields else: fields = self.sources V = LookupArray((fields, (parent, self.basis_container)), dtype=np.complex128) # define the functions to interpolate over the mesh def elec_func(r): return source_field.electric_field(s, r) def mag_func(r): return source_field.magnetic_field(s, r) for field in fields: if field in ("E", "nxE"): field_func = elec_func source_cross = field == "nxE" elif field in ("H", "nxH"): field_func = mag_func source_cross = field == "nxH" else: raise ValueError(field) for part in parent.iter_single(): basis = self.basis_container[part] V[field, part] = basis.weight_function(field_func, self.integration_rule, part.nodes, source_cross) return V