def gen_ifcs_node_mapping(parax_model, node_defs, nodes): """Create mapping between composite diagram and interface based diagram. I think this works only for singlets and mirrors. """ map_to_ifcs = [] origin = np.array([0., 0.]) for i, kernel in enumerate(node_defs): if len(kernel) == 1: idx = kernel[0] map_to_ifcs.append((idx, i, 0.)) elif len(kernel) == 2: l1_pt1 = np.array(nodes[i - 1]) l1_pt2 = np.array(nodes[i]) l2_pt1 = np.array(nodes[i]) l2_pt2 = np.array(nodes[i + 1]) prev_gap_idx, gap_idx = kernel for k in range(prev_gap_idx + 1, gap_idx + 1): pt_k = np.array(parax_model.get_pt(k)) new_node1 = np.array( get_intersect(l1_pt1, l1_pt2, origin, pt_k)) if np.allclose(new_node1, pt_k): t1 = norm(new_node1 - l1_pt1) / norm(l1_pt2 - l1_pt1) map_to_ifcs.append((k, i - 1, t1)) else: new_node2 = np.array( get_intersect(origin, pt_k, l2_pt1, l2_pt2)) if np.allclose(new_node2, pt_k): t2 = norm(new_node2 - l2_pt1) / norm(l2_pt2 - l2_pt1) map_to_ifcs.append((k, i, t2)) return map_to_ifcs
def on_edit(fig, event): buffer = 0.0025 nonlocal diagram shape = diagram.shape node = self.node if node > 0 and node < (len(shape)-2): if event.xdata is not None and event.ydata is not None: inpt = np.array([event.xdata, event.ydata]) vertex, edge, perp_edge = self.bundle # project input pt onto perp_edge ppi = np.dot(inpt - vertex, perp_edge) # constrain the input point within the range, if needed if ppi < self.pp_lim[0]: inpt = vertex + (1+buffer)*self.pp_lim[0]*perp_edge elif ppi > self.pp_lim[1]: inpt = vertex + (1-buffer)*self.pp_lim[1]*perp_edge # compute new edge vertices from intersection of adjacent # edges and line shifted parallel to the initial edge edge_pt = inpt + edge pt1 = np.array(get_intersect(shape[node-1], vertex, inpt, edge_pt)) diagram.apply_data(self.node, pt1) pt2 = np.array(get_intersect(vertex, shape[node+2], inpt, edge_pt)) diagram.apply_data(self.node+1, pt2) fig.build = 'update' fig.refresh_gui(build='update')
def on_select(fig, event): nonlocal diagram if event.xdata is None or event.ydata is None: return shape = diagram.shape self.node = node = dgm_edge.node if node > 0 and node < (len(shape)-2): inpt = np.array([event.xdata, event.ydata]) # get the virtual vertex of the combined element surfaces vertex = np.array(get_intersect(shape[node-1], shape[node], shape[node+1], shape[node+2])) edge_dir_01 = normalize(shape[node] - shape[node-1]) edge_dir_23 = normalize(shape[node+2] - shape[node+1]) # which node is closer to the input point? pt1_dist = distance_sqr_2d(shape[node], inpt) pt2_dist = distance_sqr_2d(shape[node+1], inpt) if pt1_dist < pt2_dist: self.filter = calc_coef_fct(vertex, node, edge_dir_01, node+1, edge_dir_23) else: self.filter = calc_coef_fct(vertex, node+1, edge_dir_23, node, edge_dir_01) # get direction cosines for the edge edge = normalize(shape[node+1] - shape[node]) # construct perpendicular to the edge. use this to define a # range for allowed inputs perp_edge = np.array([edge[1], -edge[0]]) self.bundle = (vertex, edge, perp_edge, edge_dir_01, edge_dir_23)
def on_select(fig, event): nonlocal diagram shape = diagram.shape self.node = node = dgm_edge.node if node > 0 and node < (len(shape)-2): # get the virtual vertex of the combined element surfaces vertex = np.array(get_intersect(shape[node-1], shape[node], shape[node+1], shape[node+2])) # get direction cosines for the edge edge = normalize(shape[node+1] - shape[node]) # construct perpendicular to the edge. use this to define a # range for allowed inputs perp_edge = np.array([edge[1], -edge[0]]) self.bundle = vertex, edge, perp_edge # measure distances along the perpendicular thru the vertex # and project the 2 outer nodes and the first vertex of the # edge pp0 = np.dot(shape[node-1]-vertex, perp_edge) pp1 = np.dot(shape[node]-vertex, perp_edge) pp3 = np.dot(shape[node+2]-vertex, perp_edge) # use the first edge vertex to calculate an allowed input range if pp1 > 0: self.pp_lim = 0, min(i for i in (pp0, pp3) if i > 0) else: self.pp_lim = max(i for i in (pp0, pp3) if i < 0), 0
def nodes_from_node_defs(parax_model, node_defs): """ produce a list of nodes given the parax_model and node_defs. `node_defs` is a list of tuples, each with either one or two indices. if there is a single index, it is to a node in `parax_model`. if there are 2 indices, the first is to the gap preceding the element; the second is to the gap following the element (also the last interface of the element). The node is calculated from the intersection of the diagram edges corresponding to these gaps. There is no guarentee that the nodes calculated here represent a physically realizable system, i.e. there may be virtual airspaces. """ nodes = [] for i, kernel in enumerate(node_defs): if len(kernel) == 1: nodes.append(parax_model.get_pt(kernel[0])) elif len(kernel) == 2: prev_gap_idx, after_gap_idx = kernel l1_pt1 = parax_model.get_pt(prev_gap_idx) l1_pt2 = parax_model.get_pt(prev_gap_idx + 1) l2_pt1 = parax_model.get_pt(after_gap_idx) l2_pt2 = parax_model.get_pt(after_gap_idx + 1) new_node = get_intersect(l1_pt1, l1_pt2, l2_pt1, l2_pt2) nodes.append(new_node) elif len(kernel) == 3: idx, prev_gap_idx, after_gap_idx = kernel nodes.append(parax_model.get_pt(idx)) return nodes
def nodes_from_node_defs(parax_model, node_defs): nodes = [] for i, kernel in enumerate(node_defs): if len(kernel) == 1: nodes.append(parax_model.get_pt(kernel[0])) elif len(kernel) == 2: prev_gap_idx, gap_idx = kernel l1_pt1 = parax_model.get_pt(prev_gap_idx) l1_pt2 = parax_model.get_pt(prev_gap_idx + 1) l2_pt1 = parax_model.get_pt(gap_idx) l2_pt2 = parax_model.get_pt(gap_idx + 1) new_node = get_intersect(l1_pt1, l1_pt2, l2_pt1, l2_pt2) nodes.append(new_node) return nodes
def replace_node_with_seq(self, node, sys_seq, pp_info): """ replaces the data at node with sys_seq """ sys = self.sys ax = self.ax pr = self.pr if len(sys_seq) == 1: sys[node] = sys_seq[0] else: opt_inv = self.opt_inv efl, pp1, ppk, ffl, bfl = pp_info[2] sys[node - 1][tau] -= pp1 / sys[node - 1][indx] # sys_seq[-1][tau] = sys[node][tau] - ppk/sys_seq[-1][indx] p0 = [ax[node][ht], pr[node][ht]] pn = [ax[node + 1][ht], pr[node + 1][ht]] slp0 = [ax[node][slp], pr[node][slp]] for n, ss in enumerate(sys_seq[:-1], start=node): sys.insert(n, ss) ax_ht = ax[n - 1][ht] + sys[n - 1][tau] * ax[n - 1][slp] ax_slp = ax[n - 1][slp] - ax_ht * sys[n][pwr] ax.insert(n, [ax_ht, ax_slp, 0.0]) pr_ht = pr[n - 1][ht] + sys[n - 1][tau] * pr[n - 1][slp] pr_slp = pr[n - 1][slp] - pr_ht * sys[n][pwr] pr.insert(n, [pr_ht, pr_slp, 0.0]) # replace the original node data ax[n + 1][slp] = slp0[0] pr[n + 1][slp] = slp0[1] sys[n + 1][pwr] = (ax[n][slp] * pr[n + 1][slp] - ax[n + 1][slp] * pr[n][slp]) / opt_inv # sys_seq[-1][pwr] p1 = [ax[n][ht], pr[n][ht]] p2 = [ax[n][ht] + ax[n][slp], pr[n][ht] + pr[n][slp]] p2int = np.array(get_intersect(p1, p2, p0, pn)) ax[n + 1][ht] = p2int[0] pr[n + 1][ht] = p2int[1] sys[n][tau] = ( (ax[n][ht] * pr[n + 1][ht] - ax[n + 1][ht] * pr[n][ht]) / opt_inv) sys[n + 1][tau] = (p2int[0] * pn[1] - pn[0] * p2int[1]) / opt_inv
def calc_scale_factors(pt1, pt2, pt_k): new_node = np.array(get_intersect(pt1, pt2, np.array([0., 0.]), pt_k)) t1 = norm(new_node - pt1) / norm(pt2 - pt1) t2 = norm(pt_k) / norm(new_node) return t1, t2