Esempio n. 1
0
def get_limits_graphs(hist: history.DB, top_nodes, sub_graphs: SubGraphs):
    """Make a dict of ax to x and y limits"""

    # x limits come from the column graph
    if sub_graphs.any_with_common_x():
        column_data = list(hist.get_all(history.ColumnResult))
        x_vals = [cd.x for cd in column_data]
        x_lims = min(x_vals), max(x_vals)

    raw_y_range = {}
    if sub_graphs.surface_absolute:
        raw_y_range[sub_graphs.surface_absolute] = _get_overall_limits(
            False, top_nodes, hist, x_lims)

    if sub_graphs.surface_deviation:
        raw_y_range[sub_graphs.surface_deviation] = _get_overall_limits(
            True, top_nodes, hist, x_lims)

    if sub_graphs.columns_strain_x:
        raw_y_range[
            sub_graphs.columns_strain_x] = hist.get_column_result_range(
                history.ContourKey.total_strain_x)

    out_d = {
        ax: (x_lims, _add_margin(y_range))
        for ax, y_range in raw_y_range.items()
    }

    return out_d
Esempio n. 2
0
def graph_force_disp(hist: history.DB, db_res_case, ax: plt.Axes):

    ax.clear()

    fd_data = list(hist.get_all(history.LoadDisplacementPoint))

    # Put marks on the final increment of each load step
    res_case_data = list(hist.get_all(history.ResultCase))
    res_case_data.sort()
    maj_to_max_res_case = dict()
    for res_case in res_case_data:
        maj_to_max_res_case[res_case.major_inc] = res_case.num

    final_minor_inc_cases = set(maj_to_max_res_case.values())

    def sort_key(ld_point: history.LoadDisplacementPoint):
        return ld_point.node_num, ld_point.load_text, ld_point.disp_text

    fd_data.sort(key=sort_key)

    for (node_num, load_text,
         disp_text), fd_points in itertools.groupby(fd_data, sort_key):
        fd_points = list(fd_points)
        x = [p.disp_val for p in fd_points]
        y = [p.load_val for p in fd_points]

        show_marker = [
            p.result_case_num in final_minor_inc_cases for p in fd_points
        ]

        ax.plot(x,
                y,
                marker='x',
                markevery=show_marker,
                label=f"{node_num} {disp_text} vs {load_text}")

        # Hacky getting DoF
        dof_iter = (dof for dof in st7.DoF if dof.rx_mz_text == load_text)
        dof = next(dof_iter)

        ax.set_xlabel(dof.disp_or_rotation_text)
        ax.set_ylabel(dof.force_or_moment_text)

        # Point to show current db res case
        x_one = [
            p.disp_val for p in fd_points if p.result_case_num == db_res_case
        ]
        y_one = [
            p.load_val for p in fd_points if p.result_case_num == db_res_case
        ]

        ax.plot(x_one,
                y_one,
                marker='o',
                markeredgecolor='tab:orange',
                markerfacecolor='tab:orange')

    ax.legend()
Esempio n. 3
0
def make_dashboard(db: history.DB):

    cases = db.get_all(history.ResultCase)
    all_views = [
        ContourView(db_case_num=case.num,
                    contour_key=history.ContourKey.prestrain_mag)
        for case in cases
    ]
Esempio n. 4
0
def _get_overall_limits(
        deviation_only: bool, top_nodes: typing.FrozenSet[int],
        hist: history.DB,
        x_lims: typing.Tuple[float, float]) -> typing.Tuple[float, float]:

    all_y_points = []
    for db_res_case in sorted(hist.get_all(history.ResultCase)):
        for line_data in _get_graph_surface_profile_data(
                deviation_only, top_nodes, hist, db_res_case.num):
            these_y_points = line_data.y_within_bounds(x_lims)
            all_y_points.extend(these_y_points)

    return min(all_y_points), max(all_y_points)
Esempio n. 5
0
def _node_of_top_line(hist: history.DB) -> typing.FrozenSet[int]:

    row_skeleton = history.NodePosition._all_nones()._replace(
        result_case_num=1)
    node_first_increment = list(hist.get_all_matching(row_skeleton))
    if not node_first_increment:
        return frozenset()

    top_y = max(n.y for n in node_first_increment)

    EPS = 1e-6
    top_row = [n for n in node_first_increment if abs(n.y - top_y) <= EPS]

    return frozenset(n.node_num for n in top_row)
Esempio n. 6
0
def graph_frame(hist: history.DB, db_res_case: int,
                contour_key: history.ContourKey, ax: plt.Axes):
    row_skeleton = history.ColumnResult._all_nones()._replace(
        result_case_num=db_res_case)
    column_data = list(hist.get_all_matching(row_skeleton))

    col_data_graph = [
        cd for cd in column_data if cd.contour_key == contour_key
    ]

    ax.clear()
    graphed_something = False

    for yielded, base_col in (
        (False, 'tab:blue'),
        (True, 'tab:orange'),
    ):
        # Fall back to NaNs, only override with the real data.
        x_to_cd = {
            cd.x: history.ColumnResult._nans_at(cd.x)
            for cd in col_data_graph
        }

        # Override with the real thing
        for cd in col_data_graph:
            if cd.yielded == yielded:
                x_to_cd[cd.x] = cd
                graphed_something = True

        res_to_plot = [cd for x, cd in sorted(x_to_cd.items())]
        x = [cd.x for cd in res_to_plot]
        y_min = [cd.minimum for cd in res_to_plot]
        y_mean = [cd.mean for cd in res_to_plot]
        y_max = [cd.maximum for cd in res_to_plot]

        ax.fill_between(x, y_min, y_max, color=base_col, alpha=0.2)
        yield_text = "Dilated" if yielded else "Undilated"
        ax.plot(x,
                y_mean,
                color=base_col,
                label=f"{contour_key.name} ({yield_text})")

    ax.legend()

    return graphed_something
Esempio n. 7
0
def _get_graph_surface_profile_data(
        deviation_only: bool, top_nodes: typing.FrozenSet[int],
        hist: history.DB, db_res_case: int) -> typing.Iterable[LineData]:

    row_skeleton = history.NodePosition._all_nones()._replace(
        result_case_num=db_res_case)
    nodes_this_increment = list(hist.get_all_matching(row_skeleton))
    nodes_top_line = [
        n for n in nodes_this_increment if n.node_num in top_nodes
    ]

    top_curve = _get_surface_curve(nodes_top_line, hist, db_res_case)

    def sort_key(n):
        return n.x

    nodes_top_line.sort(key=sort_key)

    x_data = [n.x for n in nodes_top_line]

    if deviation_only:
        y_deviation = [n.y - top_curve(n.x) for n in nodes_top_line]
        yield LineData(
            x_data, y_deviation, 'black',
            f"Top Surface Deviation from {CurveCoefficients.curve_name()}",
            '-')

    else:
        y_data = [n.y for n in nodes_top_line]
        y_fit = [top_curve(x) for x in x_data]

        yield LineData(
            x_data, y_fit, 'tab:gray',
            f"{CurveCoefficients.curve_name()} best fit: {top_curve.make_nice_name()}",
            '--')
        yield LineData(x_data, y_data, 'black', f"Top Surface", '-')
Esempio n. 8
0
def make_quadmesh(
    db: history.DB,
    contour_view: ContourView,
) -> holoviews.QuadMesh:
    """Make a single Quadmesh representation"""

    struct_data = get_regular_mesh_data(db)

    node_skeleton = history.NodePosition._all_nones()._replace(
        result_case_num=contour_view.db_case_num)
    node_coords = {
        node_pos.node_num: node_pos
        for node_pos in db.get_all_matching(node_skeleton)
    }

    # Node positions
    X = numpy.zeros(shape=struct_data.node_indices.shape)
    Y = numpy.zeros(shape=struct_data.node_indices.shape)

    for idx_x, along_y in enumerate(struct_data.node_indices):
        for idx_y, node_num in enumerate(along_y):
            X[idx_x, idx_y] = node_coords[node_num].x
            Y[idx_x, idx_y] = node_coords[node_num].y

    # The values are element centred.
    Z = numpy.zeros(shape=struct_data.elem_indices.shape)

    contour_val_skeleton = history.ContourValue._all_nones()._replace(
        result_case_num=contour_view.db_case_num,
        contour_key_num=contour_view.contour_key.value)
    contour_vals = {
        contour_val.elem_num: contour_val.value
        for contour_val in db.get_all_matching(contour_val_skeleton)
    }

    # TEMP - to get rasterize to work, need to make this node-centred.
    Z_nodal_shape = (4, ) + struct_data.node_indices.shape
    Z_nodal = numpy.empty(shape=Z_nodal_shape)
    Z_nodal[:] = numpy.nan

    for idx_x, along_y in enumerate(struct_data.elem_indices):
        for idx_y, elem_num in enumerate(along_y):
            c_val = contour_vals.get(elem_num, 0.0)
            Z[idx_x, idx_y] = c_val

            for layer, (z_nodal_idx_x, z_nodal_idx_y) in enumerate(
                    itertools.product((idx_x, idx_x + 1), (idx_y, idx_y + 1))):
                Z_nodal[layer, z_nodal_idx_x, z_nodal_idx_y] = c_val

    Z_nodal_flat = numpy.nanmean(Z_nodal, axis=0)

    qmesh = holoviews.QuadMesh((X, Y, Z_nodal_flat),
                               vdims='level',
                               group=contour_view.title)
    qmesh.options(cmap='viridis')

    qmesh.opts(aspect='equal',
               line_width=0.1,
               padding=0.1,
               colorbar=True,
               width=FIG_SIZE[0],
               height=FIG_SIZE[1])

    qmesh.data.hvplot.image('x', 'y', label=contour_view.title).options(
        width=FIG_SIZE[0], height=FIG_SIZE[1])
    return qmesh
Esempio n. 9
0
def get_regular_mesh_data(db: history.DB) -> StructuredMeshData:
    """Returns a numpy array of node indices for a structured mesh."""
    elem_conn = db.get_element_connections()

    # Step 1 - get all the edges of plates.
    all_edges = []
    min_elem_num = math.inf
    for elem_num, nodes in elem_conn.items():
        if len(nodes) != 4:
            raise ValueError("Quad4 only at the moment")

        min_elem_num = min(min_elem_num, elem_num)
        for edge_idx in range(4):
            node_idx_1 = edge_idx
            node_idx_2 = (edge_idx + 1) % 4
            one_edge = QuadPlateEdge(
                plate_num=elem_num,
                edge_idx=edge_idx,
                node_num_1=nodes[node_idx_1],
                node_num_2=nodes[node_idx_2],
            )

            all_edges.append(one_edge)

    # Step 2 - orientationA and orientationB are perpendicular - one if horizontal and one is vert but we don't know which is which yet.
    sorted_global_node_to_edge = collections.defaultdict(set)
    for one_edge in all_edges:
        sorted_global_node_to_edge[one_edge.global_sorted_nodes].add(one_edge)

    elem_to_edges = collections.defaultdict(set)
    for one_edge in all_edges:
        elem_to_edges[one_edge.plate_num].add(one_edge)

    # These are perpendicular - one if horizontal and one is vert but we don't know which is which yet.
    ORI_A = 0
    ORI_B = 1

    edge_to_ori = dict()

    # Step 3 - populate the known edges by working a "frontier"
    def discard_edge_from_working_set(one_edge):
        sorted_global_node_to_edge[one_edge.global_sorted_nodes].discard(
            one_edge)
        elem_to_edges[one_edge.plate_num].discard(one_edge)

    # Start off
    arbitrary_edge = elem_to_edges[min_elem_num].pop()
    edge_to_ori[arbitrary_edge] = ORI_A
    discard_edge_from_working_set(arbitrary_edge)
    frontier_edges = {
        arbitrary_edge,
    }

    while frontier_edges:
        new_frontier = set()
        for one_edge in frontier_edges:

            # We can tell a relative orientation from the edge index.
            new_edges_from_elem = elem_to_edges[one_edge.plate_num]
            for one_new_edge in new_edges_from_elem:
                same_ori = one_new_edge.edge_idx % 2 == one_edge.edge_idx % 2
                if same_ori:
                    this_ori = edge_to_ori[one_edge]

                else:
                    this_ori = (edge_to_ori[one_edge] + 1) % 2

                edge_to_ori[one_new_edge] = this_ori
                new_frontier.add(one_new_edge)

            # We can also tell when an edge has the same orientation if it has the same global nodes.
            new_edges_from_nodes = sorted_global_node_to_edge[
                one_edge.global_sorted_nodes]
            for one_new_edge in new_edges_from_nodes:
                this_ori = edge_to_ori[one_edge]
                edge_to_ori[one_new_edge] = this_ori
                new_frontier.add(one_new_edge)

        for one_new_edge in new_frontier:
            discard_edge_from_working_set(one_new_edge)

        frontier_edges.clear()
        frontier_edges.update(new_frontier)

    # Step 4 - find a corner
    nodes_on_edges = collections.defaultdict(set)
    for one_edge in all_edges:
        for n in [one_edge.node_num_1, one_edge.node_num_2]:
            nodes_on_edges[n].add(one_edge)

    corner_nodes = [
        node_num for node_num, edges in nodes_on_edges.items()
        if len(edges) == 2
    ]

    # Step 5 - send out "streamers" from the corner
    def make_streamer(starting_node: int, ori) -> typing.List[int]:
        """Get the nodes in a line."""

        streamer = [starting_node]
        still_going = True
        while still_going:
            frontier_node = streamer[-1]
            if len(streamer) > 1:
                backwards_node = streamer[-2]

            else:
                backwards_node = None

            next_edges = [
                edge for edge in nodes_on_edges[frontier_node]
                if edge_to_ori[edge] == ori
                and backwards_node not in edge.global_sorted_nodes
            ]
            if not next_edges:
                still_going = False

            else:
                next_edge = next_edges.pop()
                streamer.append(next_edge.opposing_node(frontier_node))

        return streamer

    arbitrary_corner_node = min(corner_nodes)
    ori_to_streamer = {
        ori: make_streamer(arbitrary_corner_node, ori)
        for ori in [ORI_A, ORI_B]
    }

    len_to_ori = {
        len(streamer): ori
        for ori, streamer in ori_to_streamer.items()
    }
    X_ORI = max(len_to_ori.items())[1]
    Y_ORI = (X_ORI + 1) % 2

    # Step 6 - Make the node indices for the structured mesh
    node_indices = numpy.zeros(shape=(len(ori_to_streamer[X_ORI]),
                                      len(ori_to_streamer[Y_ORI])),
                               dtype=int)

    for y_idx, y_starting_node in enumerate(ori_to_streamer[Y_ORI]):
        x_streamer = make_streamer(y_starting_node, X_ORI)
        node_indices[:, y_idx] = x_streamer

    # Step 7 - element indices are determined based on the 2x2 patch and the nodes.
    sorted_nodes_to_elem = {
        tuple(sorted(conn)): elem_num
        for elem_num, conn in elem_conn.items()
    }
    elem_shape = (node_indices.shape[0] - 1, node_indices.shape[1] - 1)
    elem_indices = numpy.zeros(shape=elem_shape, dtype=int)

    x_idxs = range(node_indices.shape[0] - 1)
    y_idxs = range(node_indices.shape[1] - 1)
    for x_node_idx, y_node_idx in itertools.product(x_idxs, y_idxs):
        window = node_indices[x_node_idx:x_node_idx + 2,
                              y_node_idx:y_node_idx + 2]
        sorted_nodes = tuple(sorted(window.flatten()))
        elem_num = sorted_nodes_to_elem[sorted_nodes]
        elem_indices[x_node_idx, y_node_idx] = elem_num

    return StructuredMeshData(
        node_indices=node_indices,
        elem_indices=elem_indices,
    )