def test_create_components(): va = make_vis_area() va.visualizer.terminal_graph = top.terminal_graph(make_plumbing_engine()) va.visualizer.layout_pos = { 'A': [0, 0], f'{COMPONENT_NAME}.A': [1, 1], 'B': [3, 2], f'{COMPONENT_NAME}.B': [2, 1] } va.visualizer.create_graphics() component_nodeA = GraphicsNode((1, 1), 5, 'injector_valve.A', NodeType.COMPONENT_NODE) component_nodeB = GraphicsNode((2, 1), 5, 'injector_valve.B', NodeType.COMPONENT_NODE) expected_nodes = { 'A': GraphicsNode((0, 0), 5, 'A'), 'B': GraphicsNode((3, 2), 5, 'B'), 'injector_valve.A': component_nodeA, 'injector_valve.B': component_nodeB } expected_components = { COMPONENT_NAME: GraphicsComponent(COMPONENT_NAME, [component_nodeA, component_nodeB]) } assert va.visualizer.graphics_nodes == expected_nodes assert va.visualizer.graphics_components == expected_components
def test_vis_area_component_paint(): va = make_vis_area() va.visualizer.terminal_graph = top.terminal_graph(make_plumbing_engine()) va.visualizer.layout_pos = { 'A': [0, 0], f'{COMPONENT_NAME}.A': [1, 1], 'B': [3, 2], f'{COMPONENT_NAME}.B': [2, 1] } va.visualizer.create_graphics() painter = MockPainter() r = 5 va.visualizer.paint_component(painter, COMPONENT_NAME, name=False) expected_ellipses = [(1, 1, r, r), (2, 1, r, r)] assert expected_ellipses == painter.ellipses painter.clear() va.visualizer.paint_component(painter, COMPONENT_NAME, state=True) centroid = va.visualizer.graphics_components[COMPONENT_NAME].centroid() offset = va.visualizer.graphics_components[COMPONENT_NAME].offset state = 'closed' expected_text = [(centroid[0] + offset, centroid[1] + offset, COMPONENT_NAME), (centroid[0] + offset, centroid[1] + 3 * offset, state)] assert expected_text == expected_text
def test_terminal_graph_parallel_components(): p = parallel_component_engine() t = top.terminal_graph(p) expected_t = nx.Graph([(1, 'c1.1'), ('c1.1', 'c1.2'), ('c1.2', 2), (1, 'c2.1'), ('c2.1', 'c2.2'), ('c2.2', 2)]) assert nx.is_isomorphic(t, expected_t)
def make_vis_area(): va = QMLVisualizationArea() va.setWidth(30) va.setHeight(20) plumb = make_plumbing_engine() # Assign these directly because we want to use a hardcoded layout, # not the automatic layout that uploadEngineInstance would trigger. va.visualizer.engine_instance = plumb va.visualizer.terminal_graph = top.terminal_graph(plumb) va.visualizer.components = top.component_nodes(plumb) va.visualizer.layout_pos = { 'A': [0, 0], f'{COMPONENT_NAME}.A': [1, 1], 'B': [3, 2], f'{COMPONENT_NAME}.B': [2, 1] } return va
def uploadEngineInstance(self, engine): """ Set the local engine to the input variable and initialize associated local objects. Setter and initializer for uploading an engine to be displayed. After an engine is set, the according layout and terminal graph is generated from the engine data. Parameters ---------- engine: topside.plumbing_engine An instance of the topside engine to be displayed. """ if self.DEBUG_MODE: print('New plumbing engine instance received') self.engine_instance = engine self.terminal_graph = top.terminal_graph(self.engine_instance) self.components = top.component_nodes(self.engine_instance) self.layout_pos = top.layout_plumbing_engine(self.engine_instance) self.create_graphics() self.setRescaleNeeded() self.parent().update()
def test_vis_area_scale_and_center(): va = QMLVisualizationArea() va.setWidth(30) va.setHeight(20) va.visualizer.terminal_graph = top.terminal_graph(make_plumbing_engine()) va.visualizer.layout_pos = { 'A': [0, 0], f'{COMPONENT_NAME}.A': [1, 1], 'B': [3, 2], f'{COMPONENT_NAME}.B': [2, 1] } va.visualizer.scale_and_center() # 80% box of the 30x20 rectangle gives [xmin, xmax] = [3, 27] and # [ymin, ymax] = [2, 18]. assert va.visualizer.layout_pos == { 'A': [3, 2], f'{COMPONENT_NAME}.A': [11, 10], 'B': [27, 18], f'{COMPONENT_NAME}.B': [19, 10] }
def layout_plumbing_engine(plumbing_engine): """ Given a plumbing engine, determine the best placement of components. Parameters ---------- plumbing_engine: topside.PlumbingEngine Returns ------- pos: dict dict with keys corresponding to the nodes in the terminal graph of plumbing_engine and values corresponding to the x-y point that the node should be placed at. """ t = top.terminal_graph(plumbing_engine) components = list(top.component_nodes(plumbing_engine).values()) node_indices = {n: i for i, n in enumerate(t.nodes)} neighbors = {n: [] for n in t.nodes} for cnodes in components: for n in cnodes: neighbors[n] = [v for v in t.neighbors(n) if v in cnodes] initial_pos = make_initial_pos(t.order()) stage_1_settings = OptimizerSettings(horizontal_weight=0.1) stage_1_cost_terms = make_cost_terms(t, node_indices, neighbors, stage_1_settings) stage_1_args = (stage_1_cost_terms) # TODO(jacob): Investigate if BFGS is really the best option. # Consider implementing the Hessian of the cost function in order # to try other methods (trust-exact, trust-krylov, etc.). initial_positioning_res = minimize(cost_fn, initial_pos, jac=True, method='BFGS', args=stage_1_args, options={'maxiter': 400}) if not initial_positioning_res.success: warn('Initial positioning optimization stage was unsuccessful!') constraints = make_constraints(components, node_indices) stage_2_settings = OptimizerSettings() stage_2_cost_terms = make_cost_terms(t, node_indices, neighbors, stage_2_settings) stage_2_args = (stage_2_cost_terms) fine_tuning_res = minimize(cost_fn, initial_positioning_res.x, jac=True, method='SLSQP', constraints=constraints, args=stage_2_args, options={'maxiter': 200}) if not fine_tuning_res.success: warn('Fine-tuning optimization stage was unsuccessful!') pos = top.vector_to_pos_dict(fine_tuning_res.x, node_indices) return pos
def layout_and_plot_plumbing_engine(engine): t = top.terminal_graph(engine) pos = top.layout_plumbing_engine(engine) return plot_graph(t, pos)