diff --git a/.gitignore b/.gitignore index 373ae8f..fc9fc50 100644 --- a/.gitignore +++ b/.gitignore @@ -33,29 +33,9 @@ dist/ *.iml .idea/ .vscode/settings.json -examples/.ipynb_checkpoints/exm0-checkpoint.ipynb -examples/.ipynb_checkpoints/exm1-checkpoint.ipynb -examples/.ipynb_checkpoints/exm2-checkpoint.ipynb -examples/.ipynb_checkpoints/exm3-checkpoint.ipynb -examples/.ipynb_checkpoints/exm6-checkpoint.ipynb -examples/.ipynb_checkpoints/exm7-checkpoint.ipynb +examples/.ipynb_checkpoints/* # .vscode/settings.json -examples/.ipynb_checkpoints/exm10-checkpoint.ipynb -examples/.ipynb_checkpoints/exm12-checkpoint.ipynb -examples/.ipynb_checkpoints/exm13-checkpoint.ipynb -calfem/.ipynb_checkpoints/core-checkpoint.py -calfem/.ipynb_checkpoints/geometry-checkpoint.py -calfem/.ipynb_checkpoints/mesh-checkpoint.py -calfem/.ipynb_checkpoints/vis_mpl-checkpoint.py -examples/.ipynb_checkpoints/exed1-checkpoint.py -examples/.ipynb_checkpoints/exed1-checkpoint.ui -examples/.ipynb_checkpoints/exm1-checkpoint.py -examples/.ipynb_checkpoints/exm2_mpl-checkpoint.py -examples/.ipynb_checkpoints/exm4_mpl-checkpoint.py -examples/.ipynb_checkpoints/exm7_mpl-checkpoint.py -examples/.ipynb_checkpoints/exs1-checkpoint.py -examples/.ipynb_checkpoints/Untitled1-checkpoint.ipynb -examples/.ipynb_checkpoints/exs4a-checkpoint.py +calfem/.ipynb_checkpoints/* exm6.vtk # new notebook examples diff --git a/build-package.py b/build-package.py index edc6757..9c27f0e 100644 --- a/build-package.py +++ b/build-package.py @@ -14,7 +14,7 @@ def build_package(): if __name__ == "__main__": - package_version = "3.6.4" + package_version = "3.6.5" update_setup("calfem-python", package_version, "'numpy', 'visvis', 'pyvtk', 'matplotlib', 'scipy', 'gmsh', 'qtpy', 'vedo', 'tabulate'") diff --git a/calfem-python.psproj b/calfem-python.psproj deleted file mode 100644 index 2ee6828..0000000 --- a/calfem-python.psproj +++ /dev/null @@ -1,21 +0,0 @@ -[PyScripter] -Version=4.0.0.6 - -[Project] -ClassName=TProjectRootNode -StoreRelativePaths=TRUE -ShowFileExtensions=FALSE - -[Project\ChildNodes\Node0] -ClassName=TProjectFilesNode - -[Project\ChildNodes\Node1] -ClassName=TProjectRunConfiguationsNode - -[Project\ChildNodes] -Count=2 - -[Project\ExtraPythonPath] -Item0=D:\Users\Jonas\Development\calfem-python -Count=1 - diff --git a/calfem/core.py b/calfem/core.py index 7a8ea54..c491d73 100644 --- a/calfem/core.py +++ b/calfem/core.py @@ -4166,7 +4166,7 @@ def plante(ex, ey, ep, D, eq=None): if eq is None: return Ke else: - return Ke, fe + return Ke, fe.T #--------- plane strain -------------------------------------- @@ -4190,7 +4190,7 @@ def plante(ex, ey, ep, D, eq=None): if eq is None: return Ke else: - return Ke, fe + return Ke, fe.T else: info("Error ! Check first argument, ptype=1 or 2 allowed") diff --git a/calfem/utils.py b/calfem/utils.py index b5faa41..4d84b4b 100644 --- a/calfem/utils.py +++ b/calfem/utils.py @@ -9,12 +9,13 @@ import numpy as np import calfem.core as cfc import logging as cflog +import tabulate as tab have_pyvtk = True try: import pyvtk as vtk -except: +except: have_pyvtk = False haveMatplotLib = True @@ -23,14 +24,84 @@ have_mlab = haveMlab have_matplotlib = haveMatplotLib +have_ipython = True + +try: + from IPython.core.display import display, HTML +except: + have_ipython = False + + def error(msg): cflog.error(" "+msg) - def info(msg): cflog.info(" "+msg) +def set_debug_level(level): + cflog.getLogger().setLevel(level) + +def type_of_script(): + try: + ipy_str = str(type(get_ipython())) + if 'zmqshell' in ipy_str: + return 'jupyter' + if 'terminal' in ipy_str: + return 'ipython' + except: + return 'terminal' + +def disp(msg): + if type_of_script() == 'jupyter': + display(HTML(f"{msg}")) + else: + print(msg) + +def disp_par(msg): + if type_of_script() == 'jupyter': + display(HTML(f"

{msg}

")) + else: + print(f"\nmsg\n") + +def disp_bold(msg): + if type_of_script() == 'jupyter': + display(HTML(f"{msg}")) + else: + print(f"**{msg}**") + +def disp_bold_par(msg): + if type_of_script() == 'jupyter': + display(HTML(f"

{msg}

")) + else: + print(f"\n**{msg}**\n") + +def disp_h1(msg): + if type_of_script() == 'jupyter': + display(HTML(f"

{msg}

")) + else: + print(f"\n# {msg}\n") + +def disp_h2(msg): + if type_of_script() == 'jupyter': + display(HTML(f"

{msg}

")) + else: + print(f"\n## {msg}\n") + +def disp_h3(msg): + if type_of_script() == 'jupyter': + display(HTML(f"

{msg}

")) + else: + print(f"\n## {msg}\n") + +def disp_array(a, headers=[], fmt=".4e", tablefmt="psql", showindex=False): + """ + Print a numpy array in a nice way. + """ + if type_of_script() == 'jupyter': + display(tab.tabulate(np.asarray(a), tablefmt="html", floatfmt=".4e", showindex=showindex, headers=headers)) + else: + print(tab.tabulate(np.asarray(a), tablefmt=tablefmt, floatfmt=fmt, showindex=showindex, headers=headers)) class ElementProperties(object): def __init__(self): @@ -472,7 +543,9 @@ def export_vtk_stress(filename, coords, topo, a=None, el_scalar=None, el_vec1=No el_vec2 Vector value for each element (list) """ - points = coords.tolist() + points = np.zeros([coords.shape[0], 3], dtype=np.float64) + points[:,0:2] = coords + points = points.tolist() polygons = (topo-1).tolist() displ = [] @@ -485,7 +558,7 @@ def export_vtk_stress(filename, coords, topo, a=None, el_scalar=None, el_vec1=No if a is not None: for i in range(0, len(a), 2): - displ.append([np.asscalar(a[i]), np.asscalar(a[i+1]), 0.0]) + displ.append([a[i].item(), a[i+1].item(), 0.0]) point_data = vtk.PointData(vtk.Vectors(displ, name="displacements")) diff --git a/calfem/vis_mpl.py b/calfem/vis_mpl.py index b000c13..812b980 100644 --- a/calfem/vis_mpl.py +++ b/calfem/vis_mpl.py @@ -5,6 +5,8 @@ Contains all the functions implementing visualisation routines. """ +import os + from matplotlib.transforms import Transform import numpy as np @@ -21,7 +23,9 @@ try: from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas - from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar + from matplotlib.backends.backend_qt5agg import ( + NavigationToolbar2QT as NavigationToolbar, + ) except: print("Could not import Matplotlib backends. Probarbly due to missing Qt.") @@ -32,6 +36,7 @@ g_figures = [] + def error(msg): """Log error message""" cflog.error(msg) @@ -51,11 +56,21 @@ def figure_class(): cfv_def_mappable = None - def set_mappable(mappable): global cfv_def_mappable cfv_def_mappable = mappable +cfv_block_at_show = True + +def set_block_at_show(show_block): + cfv_block_at_show = show_block + +def ioff(): + plt.ioff() + +def ion(): + plt.ion() + def colorbar(**kwargs): """Add a colorbar to current figure""" @@ -67,15 +82,18 @@ def colorbar(**kwargs): else: return plt.colorbar(**kwargs) + def axis(*args, **kwargs): """Define axis of figure (Matplotlib passthrough)""" plt.axis(*args, **kwargs) + def title(*args, **kwargs): """Define title of figure (Matplotlib passthrough)""" plt.title(*args, **kwargs) -def figure(figure=None, show=True, fig_size=(4, 3)): + +def figure(figure=None, show=True, fig_size=(6, 5.33)): """Create a visvis figure with extras.""" f = None @@ -104,7 +122,7 @@ def figure_widget(fig, parent=None): def close_all(): """Close all visvis windows.""" - plt.close('all') + plt.close("all") closeAll = close_all @@ -136,7 +154,12 @@ def camera3d(): def show_and_wait(): """Wait for plot to show""" - plt.show() + global cfv_block_at_show + + if "CFV_NO_BLOCK" in os.environ: + plt.show(block=False) + else: + plt.show(block=cfv_block_at_show) showAndWait = show_and_wait @@ -146,6 +169,7 @@ def show_and_wait_mpl(): """Wait for plot to show""" plt.show() + def show(): """Use in Qt applications""" plt.show() @@ -155,7 +179,7 @@ def show(): def set_figure_dpi(dpi): - mpl.rcParams['figure.dpi'] = dpi + mpl.rcParams["figure.dpi"] = dpi def text(text, pos, angle=0, **kwargs): @@ -168,7 +192,7 @@ def text(text, pos, angle=0, **kwargs): def ce2vf(coords, edof, dofs_per_node, el_type): - '''Duplicate code. Extracts verts, faces and verticesPerFace from input.''' + """Duplicate code. Extracts verts, faces and verticesPerFace from input.""" if np.shape(coords)[1] == 2: is_3d = False @@ -178,32 +202,35 @@ def ce2vf(coords, edof, dofs_per_node, el_type): is_3d = True verts = coords else: - raise ValueError('coords must be N-by-2 or N-by-3 array') + raise ValueError("coords must be N-by-2 or N-by-3 array") if el_type in [2, 4]: # elements with triangular faces vertices_per_face = 3 elif el_type in [3, 5, 16]: # elements with rectangular faces vertices_per_face = 4 else: # [NOTE] This covers all element types available in CALFEM plus tetrahedrons. If more element types are added it is necessary to include them here and below. - raise ValueError('element type not implemented') + raise ValueError("element type not implemented") - faces = (edof[:, 0::dofs_per_node]-1)/dofs_per_node + faces = (edof[:, 0::dofs_per_node] - 1) / dofs_per_node # 'faces' here are actually lists of nodes in elements, not in faces necessarily if the elements are in 3D. This case is handled below. if el_type in [4, 5]: # if hexahedrons or tetrahedrons: if el_type == 5: - G = np.array([[0, 3, 2, 1], - [0, 1, 5, 4], - [4, 5, 6, 7], - [2, 6, 5, 1], - [2, 3, 7, 6], - [0, 4, 7, 3]]) # G is an array that is used to decomposes hexahedrons into its component faces. - # The numbers are from the node orders (see p94 in the Gmsh manual) and each row makes one face. + G = np.array( + [ + [0, 3, 2, 1], + [0, 1, 5, 4], + [4, 5, 6, 7], + [2, 6, 5, 1], + [2, 3, 7, 6], + [0, 4, 7, 3], + ] + ) # G is an array that is used to decomposes hexahedrons into its component faces. + # The numbers are from the node orders (see p94 in the Gmsh manual) and each row makes one face. elif el_type == 4: - G = np.array([[0, 1, 2], - [0, 3, 2], - [1, 3, 2], - [0, 3, 1]]) # This G decomposes tetrahedrons into faces + G = np.array( + [[0, 1, 2], [0, 3, 2], [1, 3, 2], [0, 3, 1]] + ) # This G decomposes tetrahedrons into faces faces = np.vstack([faces[i, G] for i in range(faces.shape[0])]) elif el_type == 16: # if 8-node-quads: # The first 4 nodes are the corners of the high order quad. @@ -212,8 +239,19 @@ def ce2vf(coords, edof, dofs_per_node, el_type): return verts, np.asarray(faces, dtype=int), vertices_per_face, is_3d -def draw_mesh(coords, edof, dofs_per_node, el_type, title=None, color=(0, 0, 0), face_color=(0.8, 0.8, 0.8), node_color=(0, 0, 0), filled=False, show_nodes=False): - ''' +def draw_mesh( + coords, + edof, + dofs_per_node, + el_type, + title=None, + color=(0, 0, 0), + face_color=(0.8, 0.8, 0.8), + node_color=(0, 0, 0), + filled=False, + show_nodes=False, +): + """ Draws wire mesh of model in 2D or 3D. Returns the Mesh object that represents the mesh. Args: @@ -231,16 +269,15 @@ def draw_mesh(coords, edof, dofs_per_node, el_type, title=None, color=(0, 0, 0), Boolean. True if the view should be changed to show the whole model. Default True. title: String. Changes title of the figure. Default "Mesh". - color: + color: 3-tuple or char. Color of the wire. Defaults to black (0,0,0). Can also be given as a character in 'rgbycmkw'. face_color: 3-tuple or char. Color of the faces. Defaults to white (1,1,1). Parameter filled must be True or faces will not be drawn at all. filled: Boolean. Faces will be drawn if True. Otherwise only the wire is drawn. Default False. - ''' + """ - verts, faces, vertices_per_face, is_3d = ce2vf( - coords, edof, dofs_per_node, el_type) + verts, faces, vertices_per_face, is_3d = ce2vf(coords, edof, dofs_per_node, el_type) y = verts[:, 0] z = verts[:, 1] @@ -248,24 +285,23 @@ def draw_mesh(coords, edof, dofs_per_node, el_type, title=None, color=(0, 0, 0), values = np.zeros(faces.shape[0], float) def quatplot(y, z, quatrangles, values=[], ax=None, **kwargs): - if not ax: ax = plt.gca() yz = np.c_[y, z] v = yz[quatrangles] if filled: pc = matplotlib.collections.PolyCollection( - v, facecolor=face_color, **kwargs) + v, facecolor=face_color, **kwargs + ) else: - pc = matplotlib.collections.PolyCollection( - v, facecolor='none', **kwargs) + pc = matplotlib.collections.PolyCollection(v, facecolor="none", **kwargs) ax.add_collection(pc) ax.autoscale() return pc ax = plt.gca() - ax.set_aspect('equal') + ax.set_aspect("equal") pc = quatplot(y, z, faces, values, ax=ax, edgecolor=color) @@ -279,8 +315,19 @@ def quatplot(y, z, quatrangles, values=[], ax=None, **kwargs): drawMesh = draw_mesh -def draw_elements(ex, ey, title='', color=(0, 0, 0), face_color=(0.8, 0.8, 0.8), node_color=(0, 0, 0), line_style='solid', filled=False, closed=True, show_nodes=False): - ''' +def draw_elements( + ex, + ey, + title="", + color=(0, 0, 0), + face_color=(0.8, 0.8, 0.8), + node_color=(0, 0, 0), + line_style="solid", + filled=False, + closed=True, + show_nodes=False, +): + """ Draws wire mesh of model in 2D or 3D. Returns the Mesh object that represents the mesh. Args: @@ -298,13 +345,13 @@ def draw_elements(ex, ey, title='', color=(0, 0, 0), face_color=(0.8, 0.8, 0.8), Boolean. True if the view should be changed to show the whole model. Default True. title: String. Changes title of the figure. Default "Mesh". - color: + color: 3-tuple or char. Color of the wire. Defaults to black (0,0,0). Can also be given as a character in 'rgbycmkw'. face_color: 3-tuple or char. Color of the faces. Defaults to white (1,1,1). Parameter filled must be True or faces will not be drawn at all. filled: Boolean. Faces will be drawn if True. Otherwise only the wire is drawn. Default False. - ''' + """ # ex = [ # [x1_1, x2_1, xn_1], @@ -318,7 +365,7 @@ def draw_elements(ex, ey, title='', color=(0, 0, 0), face_color=(0.8, 0.8, 0.8), # [y1_m, y2_m, yn_m] # ] - if ex.ndim != 1: + if ex.ndim != 1: nnodes = ex.shape[1] nel = ex.shape[0] else: @@ -338,22 +385,40 @@ def draw_elements(ex, ey, title='', color=(0, 0, 0), face_color=(0.8, 0.8, 0.8), if filled: pc = matplotlib.collections.PolyCollection( - polys, facecolor=face_color, edgecolor=color, linestyle=line_style, closed=closed) + polys, + facecolor=face_color, + edgecolor=color, + linestyle=line_style, + closed=closed, + ) else: pc = matplotlib.collections.PolyCollection( - polys, facecolor='none', edgecolor=color, linestyle=line_style, closed=closed) + polys, + facecolor="none", + edgecolor=color, + linestyle=line_style, + closed=closed, + ) ax.add_collection(pc) ax.autoscale() - ax.set_aspect('equal') + ax.set_aspect("equal") if title != None: ax.set(title=title) -def draw_node_circles(ex, ey, title='', color=(0, 0, 0), face_color=(0.8, 0.8, 0.8), filled=False, marker_type='o'): - ''' +def draw_node_circles( + ex, + ey, + title="", + color=(0, 0, 0), + face_color=(0.8, 0.8, 0.8), + filled=False, + marker_type="o", +): + """ Draws wire mesh of model in 2D or 3D. Returns the Mesh object that represents the mesh. Args: @@ -371,13 +436,13 @@ def draw_node_circles(ex, ey, title='', color=(0, 0, 0), face_color=(0.8, 0.8, 0 Boolean. True if the view should be changed to show the whole model. Default True. title: String. Changes title of the figure. Default "Mesh". - color: + color: 3-tuple or char. Color of the wire. Defaults to black (0,0,0). Can also be given as a character in 'rgbycmkw'. face_color: 3-tuple or char. Color of the faces. Defaults to white (1,1,1). Parameter filled must be True or faces will not be drawn at all. filled: Boolean. Faces will be drawn if True. Otherwise only the wire is drawn. Default False. - ''' + """ # ex = [ # [x1_1, x2_1, xn_1], @@ -409,22 +474,35 @@ def draw_node_circles(ex, ey, title='', color=(0, 0, 0), face_color=(0.8, 0.8, 0 if filled: ax.scatter(x, y, color=color, marker=marker_type) else: - ax.scatter(x, y, edgecolor=color, color='none', marker=marker_type) + ax.scatter(x, y, edgecolor=color, color="none", marker=marker_type) ax.autoscale() - ax.set_aspect('equal') + ax.set_aspect("equal") if title != None: ax.set(title=title) -def draw_element_values(values, coords, edof, dofs_per_node, el_type, displacements=None, draw_elements=True, draw_undisplaced_mesh=False, magnfac=1.0, title=None, color=(0, 0, 0), node_color=(0, 0, 0)): - ''' - Draws scalar element values in 2D or 3D. +def draw_element_values( + values, + coords, + edof, + dofs_per_node, + el_type, + displacements=None, + draw_elements=True, + draw_undisplaced_mesh=False, + magnfac=1.0, + title=None, + color=(0, 0, 0), + node_color=(0, 0, 0), +): + """ + Draws scalar element values in 2D or 3D. Args: - ev: + ev: An N-by-1 array or a list of scalars. The Scalar values of the elements. ev[i] should be the value of element i. coords: @@ -436,7 +514,7 @@ def draw_element_values(values, coords, edof, dofs_per_node, el_type, displaceme dofs_per_node: Integer. Dofs per node. - el_type: + el_type: Integer. Element Type. See Gmsh manual for details. Usually 2 for triangles or 3 for quadrangles. displacements: @@ -445,15 +523,15 @@ def draw_element_values(values, coords, edof, dofs_per_node, el_type, displaceme draw_mesh: Boolean. True if mesh wire should be drawn. Default True. - draw_undisplaced_mesh: + draw_undisplaced_mesh: Boolean. True if the wire of the undisplaced mesh should be drawn on top of the displaced mesh. Default False. Use only if displacements != None. - magnfac: + magnfac: Float. Magnification factor. Displacements are multiplied by this value. Use this to make small displacements more visible. - title: + title: String. Changes title of the figure. Default "Element Values". - ''' + """ if draw_undisplaced_mesh: draw_mesh(coords, edof, dofs_per_node, el_type, color=(0.5, 0.5, 0.5)) @@ -463,20 +541,17 @@ def draw_element_values(values, coords, edof, dofs_per_node, el_type, displaceme displacements = np.reshape(displacements, (-1, coords.shape[1])) coords = np.asarray(coords + magnfac * displacements) - verts, faces, vertices_per_face, is_3d = ce2vf( - coords, edof, dofs_per_node, el_type) + verts, faces, vertices_per_face, is_3d = ce2vf(coords, edof, dofs_per_node, el_type) y = verts[:, 0] z = verts[:, 1] def quatplot(y, z, quatrangles, values=[], ax=None, **kwargs): - if not ax: ax = plt.gca() yz = np.c_[y, z] v = yz[quatrangles] - pc = matplotlib.collections.PolyCollection( - v, **kwargs) + pc = matplotlib.collections.PolyCollection(v, **kwargs) pc.set_array(np.asarray(values)) ax.add_collection(pc) @@ -485,14 +560,12 @@ def quatplot(y, z, quatrangles, values=[], ax=None, **kwargs): fig = plt.gcf() ax = plt.gca() - ax.set_aspect('equal') + ax.set_aspect("equal") if draw_elements: - pc = quatplot(y, z, faces, values, ax=ax, - edgecolor=color) + pc = quatplot(y, z, faces, values, ax=ax, edgecolor=color) else: - pc = quatplot(y, z, faces, values, ax=ax, - edgecolor=None) + pc = quatplot(y, z, faces, values, ax=ax, edgecolor=None) # pc = quatplot(y,z, np.asarray(edof-1), values, ax=ax, # edgecolor="crimson", cmap="rainbow") @@ -503,33 +576,45 @@ def quatplot(y, z, quatrangles, values=[], ax=None, **kwargs): ax.set(title=title) -def draw_displacements(a, coords, edof, dofs_per_node, el_type, draw_undisplaced_mesh=False, magnfac=-1.0, magscale=0.25, title=None, color=(0, 0, 0), node_color=(0, 0, 0)): - ''' +def draw_displacements( + a, + coords, + edof, + dofs_per_node, + el_type, + draw_undisplaced_mesh=False, + magnfac=-1.0, + magscale=0.25, + title=None, + color=(0, 0, 0), + node_color=(0, 0, 0), +): + """ Draws scalar element values in 2D or 3D. Returns the world object elementsWobject that represents the mesh. Args: - ev: + ev: An N-by-1 array or a list of scalars. The Scalar values of the elements. ev[i] should be the value of element i. - coords: + coords: An N-by-2 or N-by-3 array. Row i contains the x,y,z coordinates of node i. - edof: + edof: An E-by-L array. Element topology. (E is the number of elements and L is the number of dofs per element) - dofs_per_node: + dofs_per_node: Integer. Dofs per node. - el_type: + el_type: Integer. Element Type. See Gmsh manual for details. Usually 2 for triangles or 3 for quadrangles. - displacements: + displacements: An N-by-2 or N-by-3 array. Row i contains the x,y,z displacements of node i. - axes: + axes: Matlotlib Axes. The Axes where the model will be drawn. If unspecified the current Axes will be used, or a new Axes will be created if none exist. draw_undisplaced_mesh: Boolean. True if the wire of the undisplaced mesh should be drawn on top of the displaced mesh. Default False. Use only if displacements != None. - magnfac: + magnfac: Float. Magnification factor. Displacements are multiplied by this value. Use this to make small displacements more visible. - title: + title: String. Changes title of the figure. Default "Element Values". - ''' + """ if draw_undisplaced_mesh: draw_mesh(coords, edof, dofs_per_node, el_type, color=(0.8, 0.8, 0.8)) @@ -553,12 +638,11 @@ def draw_displacements(a, coords, edof, dofs_per_node, el_type, draw_undisplaced max_size = y_size if magnfac < 0: - magnfac = 0.25*max_size + magnfac = 0.25 * max_size coords = np.asarray(coords + magnfac * a) - verts, faces, vertices_per_face, is_3d = ce2vf( - coords, edof, dofs_per_node, el_type) + verts, faces, vertices_per_face, is_3d = ce2vf(coords, edof, dofs_per_node, el_type) y = verts[:, 0] z = verts[:, 1] @@ -566,23 +650,22 @@ def draw_displacements(a, coords, edof, dofs_per_node, el_type, draw_undisplaced values = [] def quatplot(y, z, quatrangles, values=[], ax=None, **kwargs): - if not ax: ax = plt.gca() yz = np.c_[y, z] v = yz[quatrangles] - pc = matplotlib.collections.PolyCollection( - v, **kwargs) + pc = matplotlib.collections.PolyCollection(v, **kwargs) ax.add_collection(pc) ax.autoscale() return pc ax = plt.gca() - ax.set_aspect('equal') + ax.set_aspect("equal") - pc = quatplot(y, z, faces, values, ax=ax, edgecolor=( - 0.3, 0.3, 0.3), facecolor='none') + pc = quatplot( + y, z, faces, values, ax=ax, edgecolor=(0.3, 0.3, 0.3), facecolor="none" + ) if title != None: ax.set(title=title) @@ -595,14 +678,12 @@ def create_ordered_polys(geom, N=10): o_polys = [] - for (id, (surf_name, curve_ids, holes, _, _, _)) in geom.surfaces.items(): - + for id, (surf_name, curve_ids, holes, _, _, _) in geom.surfaces.items(): polygon = np.empty((0, 3), float) polys = [] for curve_id in curve_ids: - curve_name, curve_points, _, _, _, _ = geom.curves[curve_id] points = geom.get_point_coords(curve_points) @@ -640,19 +721,15 @@ def create_ordered_polys(geom, N=10): def draw_ordered_polys(o_polys): - for poly in o_polys: - ax = plt.gca() path = mpp.Path(poly[:, 0:2]) - patch = patches.PathPatch(path, facecolor='orange', lw=1) + patch = patches.PathPatch(path, facecolor="orange", lw=1) ax.add_patch(patch) def point_in_geometry(o_polys, point): - for poly in o_polys: - path = mpp.Path(poly[:, 0:2]) inside = path.contains_points([point]) @@ -669,7 +746,7 @@ def topo_to_tri(edof): if edof.shape[1] == 3: return edof elif edof.shape[1] == 4: - new_edof = np.zeros((edof.shape[0]*2, 3), int) + new_edof = np.zeros((edof.shape[0] * 2, 3), int) new_edof[0::2, 0] = edof[:, 0] new_edof[0::2, 1] = edof[:, 1] new_edof[0::2, 2] = edof[:, 2] @@ -678,7 +755,7 @@ def topo_to_tri(edof): new_edof[1::2, 2] = edof[:, 0] return new_edof elif edof.shape[1] == 8: - new_edof = np.zeros((edof.shape[0]*6, 3), int) + new_edof = np.zeros((edof.shape[0] * 6, 3), int) new_edof[0::6, 0] = edof[:, 0] new_edof[0::6, 1] = edof[:, 4] new_edof[0::6, 2] = edof[:, 7] @@ -702,14 +779,23 @@ def topo_to_tri(edof): error("Element topology not supported.") -def draw_nodal_values_contourf(values, coords, edof, levels=12, title=None, dofs_per_node=None, el_type=None, draw_elements=False): +def draw_nodal_values_contourf( + values, + coords, + edof, + levels=12, + title=None, + dofs_per_node=None, + el_type=None, + draw_elements=False, +): """Draws element nodal values as filled contours. Element topologies supported are triangles, 4-node quads and 8-node quads.""" edof_tri = topo_to_tri(edof) ax = plt.gca() - ax.set_aspect('equal') + ax.set_aspect("equal") x, y = coords.T v = np.asarray(values) @@ -717,8 +803,7 @@ def draw_nodal_values_contourf(values, coords, edof, levels=12, title=None, dofs if draw_elements: if dofs_per_node != None and el_type != None: - draw_mesh(coords, edof, dofs_per_node, - el_type, color=(0.2, 0.2, 0.2)) + draw_mesh(coords, edof, dofs_per_node, el_type, color=(0.2, 0.2, 0.2)) else: info("dofs_per_node and el_type must be specified to draw the mesh.") @@ -726,14 +811,23 @@ def draw_nodal_values_contourf(values, coords, edof, levels=12, title=None, dofs ax.set(title=title) -def draw_nodal_values_contour(values, coords, edof, levels=12, title=None, dofs_per_node=None, el_type=None, draw_elements=False): +def draw_nodal_values_contour( + values, + coords, + edof, + levels=12, + title=None, + dofs_per_node=None, + el_type=None, + draw_elements=False, +): """Draws element nodal values as filled contours. Element topologies supported are triangles, 4-node quads and 8-node quads.""" edof_tri = topo_to_tri(edof) ax = plt.gca() - ax.set_aspect('equal') + ax.set_aspect("equal") x, y = coords.T v = np.asarray(values) @@ -741,8 +835,7 @@ def draw_nodal_values_contour(values, coords, edof, levels=12, title=None, dofs_ if draw_elements: if dofs_per_node != None and el_type != None: - draw_mesh(coords, edof, dofs_per_node, - el_type, color=(0.2, 0.2, 0.2)) + draw_mesh(coords, edof, dofs_per_node, el_type, color=(0.2, 0.2, 0.2)) else: info("dofs_per_node and el_type must be specified to draw the mesh.") @@ -750,14 +843,22 @@ def draw_nodal_values_contour(values, coords, edof, levels=12, title=None, dofs_ ax.set(title=title) -def draw_nodal_values_shaded(values, coords, edof, title=None, dofs_per_node=None, el_type=None, draw_elements=False): +def draw_nodal_values_shaded( + values, + coords, + edof, + title=None, + dofs_per_node=None, + el_type=None, + draw_elements=False, +): """Draws element nodal values as shaded triangles. Element topologies supported are triangles, 4-node quads and 8-node quads.""" edof_tri = topo_to_tri(edof) ax = plt.gca() - ax.set_aspect('equal') + ax.set_aspect("equal") x, y = coords.T v = np.asarray(values) @@ -765,8 +866,7 @@ def draw_nodal_values_shaded(values, coords, edof, title=None, dofs_per_node=Non if draw_elements: if dofs_per_node != None and el_type != None: - draw_mesh(coords, edof, dofs_per_node, - el_type, color=(0.2, 0.2, 0.2)) + draw_mesh(coords, edof, dofs_per_node, el_type, color=(0.2, 0.2, 0.2)) else: info("dofs_per_node and el_type must be specified to draw the mesh.") @@ -777,8 +877,19 @@ def draw_nodal_values_shaded(values, coords, edof, title=None, dofs_per_node=Non draw_nodal_values = draw_nodal_values_contourf -def draw_geometry(geometry, draw_points=True, label_points=True, label_curves=True, title=None, font_size=11, N=20, rel_margin=0.05, draw_axis=False, axes=None): - ''' +def draw_geometry( + geometry, + draw_points=True, + label_points=True, + label_curves=True, + title=None, + font_size=11, + N=20, + rel_margin=0.05, + draw_axis=False, + axes=None, +): + """ Draws the geometry (points and curves) in geoData Args: geoData: @@ -787,7 +898,7 @@ def draw_geometry(geometry, draw_points=True, label_points=True, label_curves=Tr Matplotlib Axes. The Axes where the model will be drawn. If unspecified the current Axes will be used, or a new Axes will be created if none exist. axes_adjust: Boolean. If True the view will be changed to show the whole model. Default True. - draw_points: + draw_points: Boolean. If True points will be drawn. label_points: Boolean. If True Points will be labeled. The format is: ID[marker]. If a point has marker==0 only the ID is written. @@ -799,19 +910,19 @@ def draw_geometry(geometry, draw_points=True, label_points=True, label_curves=Tr Integer. The number of discrete points per curve segment. Default 20. Increase for smoother curves. Decrease for better performance. rel_margin: Extra spacing between geometry and axis - ''' + """ if axes is None: ax = plt.gca() else: ax = axes - - ax.set_aspect('equal') + + ax.set_aspect("equal") ax.set_frame_on(draw_axis) if draw_points: P = np.array(geometry.getPointCoords()) # M-by-3 list of M points. - #plotArgs = {'mc':'r', 'mw':5, 'lw':0, 'ms':'o', 'axesAdjust':False, 'axes':axes} + # plotArgs = {'mc':'r', 'mw':5, 'lw':0, 'ms':'o', 'axesAdjust':False, 'axes':axes} plotArgs = {"marker": "o", "ls": ""} if geometry.is3D: plt.plot(P[:, 0], P[:, 1], P[:, 2], **plotArgs) @@ -819,14 +930,19 @@ def draw_geometry(geometry, draw_points=True, label_points=True, label_curves=Tr plt.plot(P[:, 0], P[:, 1], **plotArgs) if label_points: # Write text label at the points: - # [[x, y, z], elSize, marker] - for (ID, (xyz, el_size, marker)) in geometry.points.items(): - text = " " + str(ID) + ("[%s]" % - marker if marker is not 0 else '') - plt.text(xyz[0], xyz[1], text, - fontsize=font_size, color=(0.5, 0, 0.5)) - - for(ID, (curveName, pointIDs, marker, elementsOnCurve, _, _)) in geometry.curves.items(): + # [[x, y, z], elSize, marker] + for ID, (xyz, el_size, marker) in geometry.points.items(): + text = " " + str(ID) + ("[%s]" % marker if marker != 0 else "") + plt.text(xyz[0], xyz[1], text, fontsize=font_size, color=(0.5, 0, 0.5)) + + for ID, ( + curveName, + pointIDs, + marker, + elementsOnCurve, + _, + _, + ) in geometry.curves.items(): points = geometry.getPointCoords(pointIDs) if curveName == "Spline": P = _catmullspline(points, N) @@ -848,12 +964,12 @@ def draw_geometry(geometry, draw_points=True, label_points=True, label_curves=Tr if label_curves: # Sort of midpoint along the curve. Where the text goes. - midP = P[int(P.shape[0]*7.0/12), :].tolist() + midP = P[int(P.shape[0] * 7.0 / 12), :].tolist() # Create the text for the curve. Includes ID, elementsOnCurve, and marker: - text = " "+str(ID) - text += "(%s)" % (elementsOnCurve) if elementsOnCurve is not None else '' + text = " " + str(ID) + text += "(%s)" % (elementsOnCurve) if elementsOnCurve is not None else "" # Something like "4(5)[8]" - text += "[%s]" % (marker) if marker is not 0 else '' + text += "[%s]" % (marker) if marker != 0 else "" plt.text(midP[0], midP[1], text, fontsize=font_size) if title != None: @@ -865,19 +981,20 @@ def draw_geometry(geometry, draw_points=True, label_points=True, label_curves=Tr g_height = max_y - min_y if g_width > g_height: - margin = rel_margin*g_width + margin = rel_margin * g_width else: - margin = rel_margin*g_height + margin = rel_margin * g_height bottom, top = ax.get_ylim() left, right = ax.get_xlim() - ax.set_ylim(bottom-margin, top+margin) - ax.set_xlim(left-margin, right+margin) + ax.set_ylim(bottom - margin, top + margin) + ax.set_xlim(left - margin, right + margin) # if axesAdjust: # _adjustaxes(axes, geoData.is3D) - #axes.daspectAuto = False - #axes.daspect = (1,1,1) + # axes.daspectAuto = False + # axes.daspect = (1,1,1) + # drawGeometry = draw_geometry @@ -900,26 +1017,32 @@ def _catmullspline(controlPoints, pointsOnEachSegment=10): If there are n control points and k samplesPerSegment, then there will be (n+1)*k numeric points on the curve. """ - controlPoints = np.asarray( - controlPoints) # Convert to array if input is a list. + controlPoints = np.asarray(controlPoints) # Convert to array if input is a list. if (controlPoints[0, :] == controlPoints[-1, :]).all(): # If the curve is closed we extend each opposite endpoint to the other side - CPs = np.asmatrix(np.vstack((controlPoints[-2, :], - controlPoints, - controlPoints[1, :]))) + CPs = np.asmatrix( + np.vstack((controlPoints[-2, :], controlPoints, controlPoints[1, :])) + ) else: # Else make mirrored endpoints: - CPs = np.asmatrix(np.vstack((2*controlPoints[0, :] - controlPoints[1, :], - controlPoints, - 2*controlPoints[-1, :] - controlPoints[-2, :]))) - M = 0.5 * np.matrix([[0, 2, 0, 0], [-1, 0, 1, 0], - [2, -5, 4, -1], [-1, 3, -3, 1]]) + CPs = np.asmatrix( + np.vstack( + ( + 2 * controlPoints[0, :] - controlPoints[1, :], + controlPoints, + 2 * controlPoints[-1, :] - controlPoints[-2, :], + ) + ) + ) + M = 0.5 * np.matrix([[0, 2, 0, 0], [-1, 0, 1, 0], [2, -5, 4, -1], [-1, 3, -3, 1]]) t = np.linspace(0, 1, pointsOnEachSegment) T = np.matrix([[1, s, pow(s, 2), pow(s, 3)] for s in t]) - return np.asarray(np.vstack([T * M * CPs[j-1:j+3, :] for j in range(1, len(CPs)-2)])) + return np.asarray( + np.vstack([T * M * CPs[j - 1 : j + 3, :] for j in range(1, len(CPs) - 2)]) + ) def _bspline(controlPoints, pointsOnCurve=20): - ''' + """ Uniform cubic B-spline. Params: @@ -934,26 +1057,32 @@ def _bspline(controlPoints, pointsOnCurve=20): Based on descriptions on: http://www.siggraph.org/education/materials/HyperGraph/modeling/splines/b_spline.htm http://en.wikipedia.org/wiki/B-spline#Uniform_cubic_B-splines - ''' - controlPoints = np.asarray( - controlPoints) # Convert to array if input is a list. + """ + controlPoints = np.asarray(controlPoints) # Convert to array if input is a list. if (controlPoints[0, :] == controlPoints[-1, :]).all(): # If the curve is closed we extend each opposite endpoint to the other side - CPs = np.asmatrix(np.vstack((controlPoints[-2, :], - controlPoints, - controlPoints[1, :]))) + CPs = np.asmatrix( + np.vstack((controlPoints[-2, :], controlPoints, controlPoints[1, :])) + ) else: # Else make mirrored endpoints: - CPs = np.asmatrix(np.vstack((2*controlPoints[0, :] - controlPoints[1, :], - controlPoints, - 2*controlPoints[-1, :] - controlPoints[-2, :]))) - M = (1.0/6) * np.matrix([[-1, 3, -3, 1], - [3, -6, 3, 0], - [-3, 0, 3, 0], - [1, 4, 1, 0]]) + CPs = np.asmatrix( + np.vstack( + ( + 2 * controlPoints[0, :] - controlPoints[1, :], + controlPoints, + 2 * controlPoints[-1, :] - controlPoints[-2, :], + ) + ) + ) + M = (1.0 / 6) * np.matrix( + [[-1, 3, -3, 1], [3, -6, 3, 0], [-3, 0, 3, 0], [1, 4, 1, 0]] + ) t = np.linspace(0, 1, pointsOnCurve) T = np.matrix([[pow(s, 3), pow(s, 2), s, 1] for s in t]) - return np.asarray(np.vstack([T * M * CPs[i-1: i+3, :] for i in range(1, len(CPs)-2)])) + return np.asarray( + np.vstack([T * M * CPs[i - 1 : i + 3, :] for i in range(1, len(CPs) - 2)]) + ) def _circleArc(start, center, end, pointsOnCurve=20): @@ -961,17 +1090,26 @@ def _circleArc(start, center, end, pointsOnCurve=20): def _ellipseArc(start, center, majAxP, end, pointsOnCurve=20): - '''Input are 3D 1-by-3 numpy arrays or vectors''' + """Input are 3D 1-by-3 numpy arrays or vectors""" # First part is to find a similarity transform in 3D that transform the ellipse to # the XY-plane with the center at the origin and the major axis of the ellipse along the X-axis. # convert to arrays in case inputs are lists: - start, center, majAxP, end, = np.asarray(start), np.asarray( - center), np.asarray(majAxP), np.asarray(end) - - zPrim = np.cross(start-center, end-center) + ( + start, + center, + majAxP, + end, + ) = ( + np.asarray(start), + np.asarray(center), + np.asarray(majAxP), + np.asarray(end), + ) + + zPrim = np.cross(start - center, end - center) zPrim = zPrim / np.linalg.norm(zPrim) - xPrim = (majAxP-center) / np.linalg.norm(majAxP-center) + xPrim = (majAxP - center) / np.linalg.norm(majAxP - center) yPrim = np.cross(zPrim, xPrim) # Rotation matrix from ordinary coords to system where ellipse is in the XY-plane. (Actually hstack) @@ -993,14 +1131,19 @@ def _ellipseArc(start, center, majAxP, end, pointsOnCurve=20): # Just extract x & y from the new start and endpoints xe, ye = e[0, 0], e[1, 0] - a = np.sqrt((pow(ye*xs, 2) - pow(xe*ys, 2)) / (pow(ye, 2) - pow(ys, 2))) - b = np.sqrt((pow(ye*xs, 2) - pow(xe*ys, 2)) / ((pow(ye, 2) - pow(ys, 2)) - * ((pow(xe, 2) - pow(xs, 2)) / (pow(ys, 2) - pow(ye, 2))))) + a = np.sqrt((pow(ye * xs, 2) - pow(xe * ys, 2)) / (pow(ye, 2) - pow(ys, 2))) + b = np.sqrt( + (pow(ye * xs, 2) - pow(xe * ys, 2)) + / ( + (pow(ye, 2) - pow(ys, 2)) + * ((pow(xe, 2) - pow(xs, 2)) / (pow(ys, 2) - pow(ye, 2))) + ) + ) # atan2 is a function that goes from -pi to pi. It gives the signed angle from the X-axis to point (y,x) - ts = atan2(ys/b, xs/a) + ts = atan2(ys / b, xs / a) # We can't use the (transformed) start- and endpoints directly, but we divide x and y by the - te = atan2(ye/b, xe/a) + te = atan2(ye / b, xe / a) # ellipse minor&major axes to get the parameter t that corresponds to the point on the ellipse. # See ellipse formula: x = a * cos (t), y = b * sin(t). # So ts and te are the parameter values of the start- and endpoints (in the transformed coordinate system). @@ -1014,17 +1157,16 @@ def _ellipseArc(start, center, majAxP, end, pointsOnCurve=20): # the shortest parameter distance between start- and end-point stradles the discontinuity that jumps from pi to -pi. else: # number of points on the first length. - ps1 = round(pointsOnCurve * (pi-te)/(2*pi-te+ts)) + ps1 = round(pointsOnCurve * (pi - te) / (2 * pi - te + ts)) # number of points on the first length. - ps2 = round(pointsOnCurve * (ts+pi)/(2*pi-te+ts)) - times = np.concatenate( - (np.linspace(te, pi, ps1), np.linspace(-pi, ts, ps2))) + ps2 = round(pointsOnCurve * (ts + pi) / (2 * pi - te + ts)) + times = np.concatenate((np.linspace(te, pi, ps1), np.linspace(-pi, ts, ps2))) - ellArc = np.array([[a*cos(t), b*sin(t)] - for t in times]).T # points on arc (in 2D) + ellArc = np.array( + [[a * cos(t), b * sin(t)] for t in times] + ).T # points on arc (in 2D) # Make 3D homogenous coords by adding rows of 0s and 1s. - ellArc = np.vstack( - (ellArc, np.repeat(np.matrix([[0], [1]]), ellArc.shape[1], 1))) + ellArc = np.vstack((ellArc, np.repeat(np.matrix([[0], [1]]), ellArc.shape[1], 1))) ellArc = T * ellArc # Transform back to the original coordinate system return np.asarray(ellArc.T[:, 0:3]) # return points as an N-by-3 array. @@ -1063,7 +1205,7 @@ def eldraw2(ex, ey, plotpar=[1, 2, 1], elnum=[]): """ if ex.shape == ey.shape: - if ex.ndim !=1: + if ex.ndim != 1: nen = ex.shape[1] else: nen = ex.shape[0] @@ -1080,11 +1222,11 @@ def eldraw2(ex, ey, plotpar=[1, 2, 1], elnum=[]): # Translate CALFEM plotpar to visvis if line_type == 1: - mpl_line_style = 'solid' + mpl_line_style = "solid" elif line_type == 2: mpl_line_style = (0, (5, 5)) elif line_type == 3: - mpl_line_style = 'dotted' + mpl_line_style = "dotted" if line_color == 1: mpl_line_color = (0, 0, 0) # 'k' @@ -1096,24 +1238,24 @@ def eldraw2(ex, ey, plotpar=[1, 2, 1], elnum=[]): mpl_line_color = (1, 0, 0) # 'r' if node_mark == 1: - mpl_node_mark = 'o' + mpl_node_mark = "o" elif node_mark == 2: - mpl_node_mark = 'x' + mpl_node_mark = "x" elif node_mark == 0: - mpl_node_mark = '' + mpl_node_mark = "" - plt.axis('equal') + plt.axis("equal") draw_element_numbers = False if len(elnum) == ex.shape[0]: draw_element_numbers = True - draw_elements(ex, ey, color=mpl_line_color, - line_style=mpl_line_style, filled=False) - if mpl_node_mark != '': - draw_node_circles(ex, ey, color=mpl_line_color, - filled=False, marker_type=mpl_node_mark) + draw_elements(ex, ey, color=mpl_line_color, line_style=mpl_line_style, filled=False) + if mpl_node_mark != "": + draw_node_circles( + ex, ey, color=mpl_line_color, filled=False, marker_type=mpl_node_mark + ) return None @@ -1123,8 +1265,8 @@ def scalfact2(ex, ey, ed, rat=0.2): [sfac]=scalfact2(ex,ey,ed,rat) [sfac]=scalfact2(ex,ey,ed) ------------------------------------------------------------- - PURPOSE - Determine scale factor for drawing computational results, such as + PURPOSE + Determine scale factor for drawing computational results, such as displacements, section forces or flux. INPUT @@ -1132,7 +1274,7 @@ def scalfact2(ex, ey, ed, rat=0.2): ed: element displacement matrix or section force matrix - rat: relation between illustrated quantity and element size. + rat: relation between illustrated quantity and element size. If not specified, 0.2 is used. ------------------------------------------------------------- @@ -1152,15 +1294,15 @@ def scalfact2(ex, ey, ed, rat=0.2): # end if ex.shape == ey.shape: - if ex.ndim !=1: + if ex.ndim != 1: nen = ex.shape[1] else: nen = ex.shape[0] else: raise ValueError("Check size of ex, ey dimensions.") - dx_max = float(np.max(ex))-float(np.min(ex)) - dy_max = float(np.max(ey))-float(np.min(ey)) + dx_max = float(np.max(ex)) - float(np.min(ex)) + dy_max = float(np.max(ey)) - float(np.min(ey)) dl_max = max(dx_max, dy_max) ed_max = float(np.max(np.max(np.abs(ed)))) @@ -1170,12 +1312,11 @@ def scalfact2(ex, ey, ed, rat=0.2): k = rat - return k*dl_max/ed_max + return k * dl_max / ed_max def eliso2_mpl(ex, ey, ed): - - plt.axis('equal') + plt.axis("equal") gx = [] gy = [] @@ -1195,10 +1336,10 @@ def eliso2_mpl(ex, ey, ed): def pltstyle(plotpar): """ ------------------------------------------------------------- - PURPOSE - Define define linetype,linecolor and markertype character codes. + PURPOSE + Define define linetype,linecolor and markertype character codes. - INPUT + INPUT plotpar=[ linetype, linecolor, nodemark ] linetype=1 -> solid linecolor=1 -> black @@ -1206,9 +1347,9 @@ def pltstyle(plotpar): 3 -> dotted 3 -> magenta 4 -> red - nodemark=1 -> circle - 2 -> star - 0 -> no mark + nodemark=1 -> circle + 2 -> star + 0 -> no mark OUTPUT s1: linetype and color for mesh lines s2: type and color for node markers @@ -1222,52 +1363,53 @@ def pltstyle(plotpar): """ if type(plotpar) != list: raise TypeError("plotpar should be a list.") - if len(plotpar)!=3: + if len(plotpar) != 3: raise ValueError("plotpar needs to be a list of 3 values.") p1, p2, p3 = plotpar - s1 = '' - s2 = '' + s1 = "" + s2 = "" if p1 == 1: - s1 += '-' + s1 += "-" elif p1 == 2: - s1 += '--' + s1 += "--" elif p1 == 3: - s1 += ':' + s1 += ":" else: raise ValueError("Invalid value for plotpar[0].") if p2 == 1: - s1 += 'k' + s1 += "k" elif p2 == 2: - s1 += 'b' + s1 += "b" elif p2 == 3: - s1 += 'm' + s1 += "m" elif p2 == 4: - s1 += 'r' + s1 += "r" else: raise ValueError("Invalid value for plotpar[1].") if p3 == 1: - s2 = 'ko' + s2 = "ko" elif p3 == 2: - s2 = 'k*' + s2 = "k*" elif p3 == 3: - s2 = 'k.' + s2 = "k." else: raise ValueError("Invalid value for plotpar[2].") return s1, s2 + def pltstyle2(plotpar): """ ------------------------------------------------------------- - PURPOSE - Define define linetype,linecolor and markertype character codes. + PURPOSE + Define define linetype,linecolor and markertype character codes. - INPUT + INPUT plotpar=[ linetype, linecolor, nodemark ] linetype=1 -> solid linecolor=1 -> black @@ -1275,9 +1417,9 @@ def pltstyle2(plotpar): 3 -> dotted 3 -> magenta 4 -> red - nodemark=1 -> circle - 2 -> star - 0 -> no mark + nodemark=1 -> circle + 2 -> star + 0 -> no mark OUTPUT s1: linetype and color for mesh lines s2: type and color for node markers @@ -1295,20 +1437,20 @@ def pltstyle2(plotpar): p1, p2, p3 = plotpar - s1 = '' - s2 = '' + s1 = "" + s2 = "" - line_style = '' - line_color = '' - node_color = '' - node_type = '' + line_style = "" + line_color = "" + node_color = "" + node_type = "" if p1 == 1: - line_style = 'solid' + line_style = "solid" elif p1 == 2: line_style = (0, (5, 5)) elif p1 == 3: - line_style = 'dotted' + line_style = "dotted" else: raise ValueError("Invalid value for plotpar[0].") @@ -1325,13 +1467,13 @@ def pltstyle2(plotpar): if p3 == 1: node_color = (0, 0, 0) - node_type = 'o' + node_type = "o" elif p3 == 2: node_color = (0, 0, 0) - node_type = '*' + node_type = "*" elif p3 == 3: node_color = (0, 0, 0) - node_type = '.' + node_type = "." else: raise ValueError("Invalid value for plotpar[2].") @@ -1344,32 +1486,32 @@ def eldisp2(ex, ey, ed, plotpar=[2, 1, 1], sfac=None): [sfac]=eldisp2(ex,ey,ed,plotpar) [sfac]=eldisp2(ex,ey,ed) ------------------------------------------------------------- - PURPOSE - Draw the deformed 2D mesh for a number of elements of + PURPOSE + Draw the deformed 2D mesh for a number of elements of the same type. Supported elements are: - 1) -> bar element 2) -> beam el. - 3) -> triangular 3 node el. 4) -> quadrilateral 4 node el. + 1) -> bar element 2) -> beam el. + 3) -> triangular 3 node el. 4) -> quadrilateral 4 node el. 5) -> 8-node isopar. element INPUT ex,ey:.......... nen: number of element nodes - nel: number of elements + nel: number of elements ed: element displacement matrix - plotpar=[ linetype, linecolor, nodemark] + plotpar=[ linetype, linecolor, nodemark] linetype=1 -> solid linecolor=1 -> black 2 -> dashed 2 -> blue 3 -> dotted 3 -> magenta 4 -> red - nodemark=1 -> circle - 2 -> star - 0 -> no mark + nodemark=1 -> circle + 2 -> star + 0 -> no mark - sfac: scale factor for displacements + sfac: scale factor for displacements - Rem. Default if sfac and plotpar is left out is auto magnification - and dashed black lines with circles at nodes -> plotpar=[2 1 1] + Rem. Default if sfac and plotpar is left out is auto magnification + and dashed black lines with circles at nodes -> plotpar=[2 1 1] ------------------------------------------------------------- LAST MODIFIED: O Dahlblom 2004-10-01 @@ -1382,7 +1524,7 @@ def eldisp2(ex, ey, ed, plotpar=[2, 1, 1], sfac=None): """ if ex.shape == ey.shape: - if ex.ndim !=1: + if ex.ndim != 1: nen = ex.shape[1] if ed.shape[0] != ex.shape[0]: @@ -1399,29 +1541,28 @@ def eldisp2(ex, ey, ed, plotpar=[2, 1, 1], sfac=None): else: raise ValueError("Check size of ex, ey dimensions.") - - dx_max = float(np.max(ex))-float(np.min(ex)) - dy_max = float(np.max(ey))-float(np.min(ey)) + dx_max = float(np.max(ex)) - float(np.min(ex)) + dy_max = float(np.max(ey)) - float(np.min(ey)) dl_max = max(dx_max, dy_max) ed_max = float(np.max(np.max(np.abs(ed)))) krel = 0.1 if sfac is None: - sfac = krel*dl_max/ed_max + sfac = krel * dl_max / ed_max k = sfac line_color, line_style, node_color, node_style = pltstyle2(plotpar) - + if nen == 2: if ned == 4: - x = np.transpose(ex + k*ed[:, [0, 2]]) - y = np.transpose(ey + k*ed[:, [1, 3]]) + x = np.transpose(ex + k * ed[:, [0, 2]]) + y = np.transpose(ey + k * ed[:, [1, 3]]) xc = np.transpose(x) yc = np.transpose(y) elif ned == 6: - x = np.transpose(ex + k*ed[:, [0, 3]]) - y = np.transpose(ey + k*ed[:, [1, 4]]) + x = np.transpose(ex + k * ed[:, [0, 3]]) + y = np.transpose(ey + k * ed[:, [1, 4]]) exc, eyc = beam2crd(ex, ey, ed, k) xc = exc yc = eyc @@ -1435,13 +1576,12 @@ def eldisp2(ex, ey, ed, plotpar=[2, 1, 1], sfac=None): print("Error: Element type is not supported.") return - draw_elements(xc, yc, color=line_color, - line_style=line_style, filled=False, closed=False) - - if node_style != '': - draw_node_circles(x, y, color=node_color, - filled=False, marker_type=node_style) + draw_elements( + xc, yc, color=line_color, line_style=line_style, filled=False, closed=False + ) + if node_style != "": + draw_node_circles(x, y, color=node_color, filled=False, marker_type=node_style) # % ********** Bar or Beam elements ************* @@ -1584,45 +1724,45 @@ def eldisp2(ex, ey, ed, plotpar=[2, 1, 1], sfac=None): def dispbeam2(ex, ey, edi, plotpar=[2, 1, 1], sfac=None): """ - dispbeam2(ex,ey,edi,plotpar,sfac) - [sfac]=dispbeam2(ex,ey,edi) - [sfac]=dispbeam2(ex,ey,edi,plotpar) ------------------------------------------------------------------------- - PURPOSE - Draw the displacement diagram for a two dimensional beam element. - - INPUT: ex = [ x1 x2 ] - ey = [ y1 y2 ] element node coordinates. - - edi = [ u1 v1; - u2 v2; - .....] matrix containing the displacements - in Nbr evaluation points along the beam. - - plotpar=[linetype, linecolour, nodemark] - - linetype=1 -> solid linecolour=1 -> black - 2 -> dashed 2 -> blue - 3 -> dotted 3 -> magenta - 4 -> red - nodemark=0 -> no mark - 1 -> circle - 2 -> star - 3 -> point - - sfac = [scalar] scale factor for displacements. - - Rem. Default if sfac and plotpar is left out is auto magnification - and dashed black lines with circles at nodes -> plotpar=[1 1 1] ------------------------------------------------------------------------- - - LAST MODIFIED: O Dahlblom 2015-11-18 - O Dahlblom 2023-01-31 (Python) - - Copyright (c) Division of Structural Mechanics and - Division of Solid Mechanics. - Lund University ------------------------------------------------------------------------- + dispbeam2(ex,ey,edi,plotpar,sfac) + [sfac]=dispbeam2(ex,ey,edi) + [sfac]=dispbeam2(ex,ey,edi,plotpar) + ------------------------------------------------------------------------ + PURPOSE + Draw the displacement diagram for a two dimensional beam element. + + INPUT: ex = [ x1 x2 ] + ey = [ y1 y2 ] element node coordinates. + + edi = [ u1 v1; + u2 v2; + .....] matrix containing the displacements + in Nbr evaluation points along the beam. + + plotpar=[linetype, linecolour, nodemark] + + linetype=1 -> solid linecolour=1 -> black + 2 -> dashed 2 -> blue + 3 -> dotted 3 -> magenta + 4 -> red + nodemark=0 -> no mark + 1 -> circle + 2 -> star + 3 -> point + + sfac = [scalar] scale factor for displacements. + + Rem. Default if sfac and plotpar is left out is auto magnification + and dashed black lines with circles at nodes -> plotpar=[1 1 1] + ------------------------------------------------------------------------ + + LAST MODIFIED: O Dahlblom 2015-11-18 + O Dahlblom 2023-01-31 (Python) + + Copyright (c) Division of Structural Mechanics and + Division of Solid Mechanics. + Lund University + ------------------------------------------------------------------------ """ if ex.shape != ey.shape: raise ValueError("Check size of ex, ey dimensions.") @@ -1630,45 +1770,44 @@ def dispbeam2(ex, ey, edi, plotpar=[2, 1, 1], sfac=None): rows, cols = edi.shape if cols != 2: raise ValueError("Check size of edi dimension.") - Nbr = rows + Nbr = rows x1, x2 = ex y1, y2 = ey - dx = x2-x1 - dy = y2-y1 - L = np.sqrt(dx*dx+dy*dy) - nxX=dx/L - nyX=dy/L + dx = x2 - x1 + dy = y2 - y1 + L = np.sqrt(dx * dx + dy * dy) + nxX = dx / L + nyX = dy / L n = np.array([nxX, nyX]) - + line_color, line_style, node_color, node_style = pltstyle2(plotpar) - + if sfac is None: - sfac=(0.1*L)/(np.max(abs(edi))) - - eci = np.arange(0., L+L/(Nbr-1), L/(Nbr-1)).reshape(Nbr,1) - - edi1=edi*sfac -# From local x-coordinates to global coordinates of the beam element. - A = np.zeros(2*Nbr).reshape(Nbr,2) - A[0,0] = ex[0] - A[0,1] = ey[0] + sfac = (0.1 * L) / (np.max(abs(edi))) + + eci = np.arange(0.0, L + L / (Nbr - 1), L / (Nbr - 1)).reshape(Nbr, 1) + + edi1 = edi * sfac + # From local x-coordinates to global coordinates of the beam element. + A = np.zeros(2 * Nbr).reshape(Nbr, 2) + A[0, 0] = ex[0] + A[0, 1] = ey[0] for i in range(1, Nbr): - A[i,0]=A[0,0]+eci[i]*n[0] - A[i,1]=A[0,1]+eci[i]*n[1] + A[i, 0] = A[0, 0] + eci[i] * n[0] + A[i, 1] = A[0, 1] + eci[i] * n[1] for i in range(0, Nbr): - A[i,0]=A[i,0]+edi1[i,0]*n[0]-edi1[i,1]*n[1] - A[i,1]=A[i,1]+edi1[i,0]*n[1]+edi1[i,1]*n[0] - xc=np.array(A[:,0]) - yc=np.array(A[:,1]) + A[i, 0] = A[i, 0] + edi1[i, 0] * n[0] - edi1[i, 1] * n[1] + A[i, 1] = A[i, 1] + edi1[i, 0] * n[1] + edi1[i, 1] * n[0] + xc = np.array(A[:, 0]) + yc = np.array(A[:, 1]) + + plt.plot(xc, yc, color=line_color, linewidth=1) - plt.plot(xc,yc, color=line_color, linewidth=1) - - A1=np.array([A[0,0], A[Nbr-1,0]]).reshape(1,2) - A2=np.array([A[0,1], A[Nbr-1,1]]).reshape(1,2) - draw_node_circles(A1, A2, color=node_color, - filled=False, marker_type=node_style) + A1 = np.array([A[0, 0], A[Nbr - 1, 0]]).reshape(1, 2) + A2 = np.array([A[0, 1], A[Nbr - 1, 1]]).reshape(1, 2) + draw_node_circles(A1, A2, color=node_color, filled=False, marker_type=node_style) def secforce2(ex, ey, es, plotpar=[2, 1], sfac=None, eci=None): @@ -1677,33 +1816,33 @@ def secforce2(ex, ey, es, plotpar=[2, 1], sfac=None, eci=None): secforce2(ex,ey,es,plotpar,sfac,eci) [sfac]=secforce2(ex,ey,es) [sfac]=secforce2(ex,ey,es,plotpar) --------------------------------------------------------------------------- - PURPOSE: + -------------------------------------------------------------------------- + PURPOSE: Draw section force diagram for a two dimensional bar or beam element. - + INPUT: ex = [ x1 x2 ] - ey = [ y1 y2 ] element node coordinates. + ey = [ y1 y2 ] element node coordinates. - es = [ S1; + es = [ S1; S2; - ... ] vector containing the section force - in Nbr evaluation points along the element. - - plotpar=[linecolour, elementcolour] - + ... ] vector containing the section force + in Nbr evaluation points along the element. + + plotpar=[linecolour, elementcolour] + linecolour=1 -> black elementcolour=1 -> black 2 -> blue 2 -> blue 3 -> magenta 3 -> magenta 4 -> red 4 -> red - - sfac = [scalar] scale factor for section force diagrams. + + sfac = [scalar] scale factor for section force diagrams. eci = [ x1; x2; ... ] local x-coordinates of the evaluation points (Nbr). If not given, the evaluation points are assumed to be uniformly distributed --------------------------------------------------------------------------- + -------------------------------------------------------------------------- LAST MODIFIED: O Dahlblom 2019-12-16 O Dahlblom 2023-01-31 (Python) @@ -1711,28 +1850,28 @@ def secforce2(ex, ey, es, plotpar=[2, 1], sfac=None, eci=None): Copyright (c) Division of Structural Mechanics and Division of Solid Mechanics. Lund University --------------------------------------------------------------------------- + -------------------------------------------------------------------------- """ if ex.shape != ey.shape: raise ValueError("Check size of ex, ey dimensions.") - c=len(es) - Nbr=c + c = len(es) + Nbr = c x1, x2 = ex y1, y2 = ey - dx = x2-x1 - dy = y2-y1 - L = np.sqrt(dx*dx+dy*dy) - nxX=dx/L - nyX=dy/L + dx = x2 - x1 + dy = y2 - y1 + L = np.sqrt(dx * dx + dy * dy) + nxX = dx / L + nyX = dy / L n = np.array([nxX, nyX]) if sfac is None: - sfac=(0.2*L)/max(abs(es)) + sfac = (0.2 * L) / max(abs(es)) if eci is None: - eci = np.arange(0., L+L/(Nbr-1), L/(Nbr-1)).reshape(Nbr,1) + eci = np.arange(0.0, L + L / (Nbr - 1), L / (Nbr - 1)).reshape(Nbr, 1) p1 = plotpar[0] if p1 == 1: @@ -1745,7 +1884,7 @@ def secforce2(ex, ey, es, plotpar=[2, 1], sfac=None, eci=None): line_color = (1, 0, 0) else: raise ValueError("Invalid value for plotpar[1].") - line_style = 'solid' + line_style = "solid" p2 = plotpar[1] if p2 == 1: @@ -1763,59 +1902,58 @@ def secforce2(ex, ey, es, plotpar=[2, 1], sfac=None, eci=None): if a != c: raise ValueError("Check size of eci dimension.") - es=es*sfac + es = es * sfac -# From local x-coordinates to global coordinates of the element - A = np.zeros(2*Nbr).reshape(Nbr,2) - A[0,0] = ex[0] - A[0,1] = ey[0] + # From local x-coordinates to global coordinates of the element + A = np.zeros(2 * Nbr).reshape(Nbr, 2) + A[0, 0] = ex[0] + A[0, 1] = ey[0] for i in range(Nbr): - A[i,0] = A[0,0]+eci[i]*n[0] - A[i,1] = A[0,1]+eci[i]*n[1] + A[i, 0] = A[0, 0] + eci[i] * n[0] + A[i, 1] = A[0, 1] + eci[i] * n[1] - B=np.array(A) - -# Plot diagram + B = np.array(A) + + # Plot diagram for i in range(0, Nbr): - A[i,0]=A[i,0]+es[i]*n[1] - A[i,1]=A[i,1]-es[i]*n[0] + A[i, 0] = A[i, 0] + es[i] * n[1] + A[i, 1] = A[i, 1] - es[i] * n[0] + + xc = np.array(A[:, 0]) + yc = np.array(A[:, 1]) - xc=np.array(A[:,0]) - yc=np.array(A[:,1]) + plt.plot(xc, yc, color=line_color, linewidth=1) - plt.plot(xc,yc, color=line_color, linewidth=1) - -# Plot stripes in diagram - xs = np.zeros(2) - ys = np.zeros(2) + # Plot stripes in diagram + xs = np.zeros(2) + ys = np.zeros(2) for i in range(Nbr): - xs[0]=B[i,0] - xs[1]=A[i,0] - ys[0]=B[i,1] - ys[1]=A[i,1] - print("i,xs,ys=") - print(i,xs,ys) - plt.plot(xs,ys, color=line_color, linewidth=1) -# Plot element - plt.plot(ex,ey, color=line_color1, linewidth=2) - + xs[0] = B[i, 0] + xs[1] = A[i, 0] + ys[0] = B[i, 1] + ys[1] = A[i, 1] + plt.plot(xs, ys, color=line_color, linewidth=1) + + # Plot element + plt.plot(ex, ey, color=line_color1, linewidth=2) + def scalgraph2(sfac, magnitude, plotpar=2): """ scalgraph2(sfac, magnitude, plotpar) scalgraph2(sfac, magnitude) ------------------------------------------------------------- - PURPOSE + PURPOSE Draw a graphic scale INPUT: sfac = [scalar] scale factor. - magnitude = [Ref x y] The graphic scale has a length equivalent - to Ref and starts at coordinates (x,y). - If no coordinates are given the starting + magnitude = [Ref x y] The graphic scale has a length equivalent + to Ref and starts at coordinates (x,y). + If no coordinates are given the starting point will be (0,-0.5). - plotpar=[linecolor] + plotpar=[linecolor] linecolor=1 -> black 2 -> blue 3 -> magenta @@ -1831,23 +1969,16 @@ def scalgraph2(sfac, magnitude, plotpar=2): ------------------------------------------------------------- """ cols = len(magnitude) - if cols != 1 and cols != 3 : + if cols != 1 and cols != 3: raise ValueError("Check size of magnitude input argument.") - if cols == 1 : + if cols == 1: N = magnitude x = 0 y = -0.5 - if cols == 3 : + if cols == 3: N, x, y = magnitude - - print("x= y=") - print(x,y) - L = N*sfac - print("L=") - print(L) - print("plotpar") - print(plotpar) + L = N * sfac if plotpar == 1: line_color = (0, 0, 0) @@ -1859,10 +1990,10 @@ def scalgraph2(sfac, magnitude, plotpar=2): line_color = (1, 0, 0) else: raise ValueError("Invalid value for plotpar[1].") - print("x=") - print(x) - plt.plot([x, (x+L)],[y, y], color=line_color, linewidth=1) - plt.plot([x, x],[(y-L/20), (y+L/20)], color=line_color, linewidth=1) - plt.plot([(x+L), (x+L)],[(y-L/20), (y+L/20)], color=line_color, linewidth=1) - plt.text(x+L*1.1, (y-L/20), str(N)) + plt.plot([x, (x + L)], [y, y], color=line_color, linewidth=1) + plt.plot([x, x], [(y - L / 20), (y + L / 20)], color=line_color, linewidth=1) + plt.plot( + [(x + L), (x + L)], [(y - L / 20), (y + L / 20)], color=line_color, linewidth=1 + ) + plt.text(x + L * 1.1, (y - L / 20), str(N)) diff --git a/examples/.ipynb_checkpoints/Untitled-checkpoint.ipynb b/examples/.ipynb_checkpoints/Untitled-checkpoint.ipynb deleted file mode 100644 index 2fd6442..0000000 --- a/examples/.ipynb_checkpoints/Untitled-checkpoint.ipynb +++ /dev/null @@ -1,6 +0,0 @@ -{ - "cells": [], - "metadata": {}, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/.ipynb_checkpoints/exs1-checkpoint.ipynb b/examples/.ipynb_checkpoints/exs1-checkpoint.ipynb deleted file mode 100644 index 2fd6442..0000000 --- a/examples/.ipynb_checkpoints/exs1-checkpoint.ipynb +++ /dev/null @@ -1,6 +0,0 @@ -{ - "cells": [], - "metadata": {}, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/.ipynb_checkpoints/exs2-checkpoint.ipynb b/examples/.ipynb_checkpoints/exs2-checkpoint.ipynb deleted file mode 100644 index 0fc6225..0000000 --- a/examples/.ipynb_checkpoints/exs2-checkpoint.ipynb +++ /dev/null @@ -1,273 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Example 2: One-dimensional heat flow (exs2.py) " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This example is from the CALFEM manual.\n", - "\n", - "**Purpose:**\n", - "\n", - "Analysis of one-dimensional heat flow.\n", - "\n", - "**Description:**\n", - "\n", - "Consider a wall built up of concrete and thermal insulation. The outdoor\n", - "temperature is −17 ◦C and the temperature inside is 20 ◦C. At the inside of\n", - "the thermal insulation there is a heat source yielding $10 ~W/m^2$.\n", - "\n", - "![](./images/exs2.png)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The wall is subdivided into five elements and the one-dimensional spring\n", - "(analogy) element `spring1e` is used. Equivalent spring stiffnesses are\n", - "$k_i = λ A/L$ for thermal conductivity and $k_i = A/R$ for thermal\n", - "surface resistance. Corresponding spring stiffnesses per $m^2$ of the wall\n", - "are:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\\begin{align}\n", - "k_1 &= 1/0.04 = 25.0 ~W/K \\\\\n", - "k_2 &= 1.7/0.070 = 24.3 ~W/K \\\\\n", - "k_3 &= 0.040/0.100 = 0.4 ~W/K \\\\\n", - "k_4 &= 1.7/0.100 = 17.0 ~W/K \\\\\n", - "k_5 &= 1/0.13 = 7.7 ~W/K \n", - "\\end{align}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A global system matrix K and a heat flow vector f are defined. The heat source\n", - "inside the wall is considered by setting $f_4 = 10$. The element matrices\n", - "`Ke` are computed using `spring1e`, and the function `assem` assembles the\n", - "global stiffness matrix.\n", - "\n", - "The system of equations is solved using `solveq` with considerations to the\n", - "boundary conditions in `bc` and `bcVal`. The prescribed temperatures are \n", - "$T_1 = −17 ~^{\\circ}C$ and $T_2 = 20~^{\\circ}C$." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Necessary modules are first imported." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import calfem.core as cfc" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, the element topology is defined" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "Edof = np.array([\n", - " [1,2],\n", - " [2,3],\n", - " [3,4],\n", - " [4,5],\n", - " [5,6]\n", - "])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create stiffness matrix K and load vector f" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "K = np.mat(np.zeros((6,6)))\n", - "f = np.mat(np.zeros((6,1)))\n", - "f[3] = 10.0" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Define element properties (ep) and create element matrices for the different material layers." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "ep1 = 25.0\n", - "ep2 = 24.3\n", - "ep3 = 0.4\n", - "ep4 = 17.0\n", - "ep5 = 7.7" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Element stiffness matrices" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "Ke1 = cfc.spring1e(ep1)\n", - "Ke2 = cfc.spring1e(ep2)\n", - "Ke3 = cfc.spring1e(ep3)\n", - "Ke4 = cfc.spring1e(ep4)\n", - "Ke5 = cfc.spring1e(ep5)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Assemble all element matrices into the global stiffness matrix" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Stiffness matrix K:\n", - "[[ 25. -25. 0. 0. 0. 0. ]\n", - " [-25. 49.3 -24.3 0. 0. 0. ]\n", - " [ 0. -24.3 24.7 -0.4 0. 0. ]\n", - " [ 0. 0. -0.4 17.4 -17. 0. ]\n", - " [ 0. 0. 0. -17. 24.7 -7.7]\n", - " [ 0. 0. 0. 0. -7.7 7.7]]\n" - ] - } - ], - "source": [ - "cfc.assem(Edof[0,:], K, Ke1)\n", - "cfc.assem(Edof[1,:], K, Ke2) \n", - "cfc.assem(Edof[2,:], K, Ke3)\n", - "cfc.assem(Edof[3,:], K, Ke4)\n", - "cfc.assem(Edof[4,:], K, Ke5)\n", - "\n", - "print(\"Stiffness matrix K:\")\n", - "print(K)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Define the boundary conditions and solve the system of equations" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Displacements a:\n", - "[[-17. ]\n", - " [-16.43842455]\n", - " [-15.86067203]\n", - " [ 19.23779344]\n", - " [ 19.47540439]\n", - " [ 20. ]]\n", - "Reaction forces r:\n", - "[[-1.40393862e+01]\n", - " [ 0.00000000e+00]\n", - " [ 0.00000000e+00]\n", - " [ 0.00000000e+00]\n", - " [ 5.68434189e-14]\n", - " [ 4.03938619e+00]]\n" - ] - } - ], - "source": [ - "bc = np.array([1,6])\n", - "bcVal = np.array([-17.0, 20.0])\n", - "a,r = cfc.solveq(K, f, bc, bcVal)\n", - "\n", - "print(\"Displacements a:\")\n", - "print(a)\n", - "\n", - "print(\"Reaction forces r:\")\n", - "print(r)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.13" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/.ipynb_checkpoints/exs3-checkpoint.ipynb b/examples/.ipynb_checkpoints/exs3-checkpoint.ipynb deleted file mode 100644 index 2fd6442..0000000 --- a/examples/.ipynb_checkpoints/exs3-checkpoint.ipynb +++ /dev/null @@ -1,6 +0,0 @@ -{ - "cells": [], - "metadata": {}, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/ex_beam2.py b/examples/ex_beam2.py deleted file mode 100644 index 91608eb..0000000 --- a/examples/ex_beam2.py +++ /dev/null @@ -1,153 +0,0 @@ -# -*- coding: utf-8 -*- -# -# example exs6 -# ---------------------------------------------------------------- -# PURPOSE -# Analysis of a plane frame. -# ---------------------------------------------------------------- - -# REFERENCES -# Göran Sandberg 94-03-08 -# Karl-Gunnar Olsson 95-09-28 -# Anders Olsson 99-03-01 -# Ola Dahlblom 2004-09-14 -# ---------------------------------------------------------------- - -import numpy as np -import calfem.core as cfc -import calfem.utils as cfu -import calfem.vis_mpl as cfv - -# ----- Topology ------------------------------------------------- - -edof = np.array([ - [4, 5, 6, 1, 2, 3], - [7, 8, 9, 10, 11, 12], - [4, 5, 6, 7, 8, 9] -]) - -# ----- Stiffness matrix K and load vector f --------------------- - -K = np.matrix(np.zeros((12, 12))) -f = np.matrix(np.zeros((12, 1))) -f[3] = 2e+3 - -# ----- Element stiffness and element load matrices ------------- - -E = 200e9 -A1 = 2e-3 -A2 = 6e-3 -I1 = 1.6e-5 -I2 = 5.4e-5 - -ep1 = np.array([E, A1, I1]) -ep3 = np.array([E, A2, I2]) -ex1 = np.array([0, 0]) -ex2 = np.array([6, 6]) -ex3 = np.array([0, 6]) -ey1 = np.array([4, 0]) -ey2 = np.array([4, 0]) -ey3 = np.array([4, 4]) -eq1 = np.array([0, 0]) -eq2 = np.array([0, 0]) -eq3 = np.array([0, -10e+3]) - -Ke1 = cfc.beam2e(ex1, ey1, ep1) -Ke2 = cfc.beam2e(ex2, ey2, ep1) -Ke3, fe3 = cfc.beam2e(ex3, ey3, ep3, eq3) - -# ----- Assemble Ke into K --------------------------------------- - -cfc.assem(edof[0, :], K, Ke1) -cfc.assem(edof[1, :], K, Ke2) -cfc.assem(edof[2, :], K, Ke3, f, fe3) - -# ----- Solve the system of equations and compute reactions ------ - -bc = np.array([1, 2, 3, 10, 11]) -a, r = cfc.solveq(K, f, bc) - -print("a = ") -print(a) -print("r = ") -print(r) - -# ----- Section forces ------------------------------------------- - -ed = cfc.extract_ed(edof, a) - -es1, ed1, ec1 = cfc.beam2s(ex1, ey1, ep1, ed[0, :], eq1, nep=21) -es2, ed2, ec2 = cfc.beam2s(ex2, ey2, ep1, ed[1, :], eq2, nep=21) -es3, ed3, ec3 = cfc.beam2s(ex3, ey3, ep3, ed[2, :], eq3, nep=21) - -print("es1 = ") -print(es1) -print("es2 = ") -print(es2) -print("es3 = ") -print(es3) - -# ----- Draw deformed frame --------------------------------------- - -ex = np.array([ - ex1, ex2, ex3 -]) -print(ex) - -ey = np.array([ - ey1, ey2, ey3 -]) -print(ey) - -plotpar = [2, 1, 0] -sfac = cfv.scalfact2(ex3, ey3, ed[2, :], 0.1) - -cfv.figure(1) -cfv.eldraw2(ex1, ey1, plotpar) -cfv.eldraw2(ex2, ey2, plotpar) -cfv.eldraw2(ex3, ey3, plotpar) - -plotpar = [1, 2, 1] -cfv.eldisp2(ex1, ey1, ed[0, :], plotpar, sfac) -cfv.eldisp2(ex2, ey2, ed[1, :], plotpar, sfac) -cfv.eldisp2(ex3, ey3, ed[2, :], plotpar, sfac) -cfv.axis([-1.5, 7.5, -0.5, 5.5]) -# pltscalb2(sfac,[1e-2 0.5 0]); -cfv.title('displacements') -cfv.showAndWait() - -# ----- Draw normal force diagram -------------------------------- - -# figure(2) -# plotpar=[2 1]; -# sfac=scalfact2(ex1,ey1,es1(:,1),0.2); -# eldia2(ex1,ey1,es1(:,1),plotpar,sfac); -# eldia2(ex2,ey2,es2(:,1),plotpar,sfac); -# eldia2(ex3,ey3,es3(:,1),plotpar,sfac); -# axis([-1.5 7.5 -0.5 5.5]); -# pltscalb2(sfac,[3e4 1.5 0]); -#title('normal force') - -# ----- Draw shear force diagram --------------------------------- - -# figure(3) -# plotpar=[2 1]; -# sfac=scalfact2(ex3,ey3,es3(:,2),0.2); -# eldia2(ex1,ey1,es1(:,2),plotpar,sfac); -# eldia2(ex2,ey2,es2(:,2),plotpar,sfac); -# eldia2(ex3,ey3,es3(:,2),plotpar,sfac); -# axis([-1.5 7.5 -0.5 5.5]); -# pltscalb2(sfac,[3e4 0.5 0]); -#title('shear force') - -# ----- Draw moment diagram -------------------------------------- - -# figure(4) -# plotpar=[2 1]; -# sfac=scalfact2(ex3,ey3,es3(:,3),0.2); -# eldia2(ex1,ey1,es1(:,3),plotpar,sfac); -# eldia2(ex2,ey2,es2(:,3),plotpar,sfac); -# eldia2(ex3,ey3,es3(:,3),plotpar,sfac); -# axis([-1.5 7.5 -0.5 5.5]); -# pltscalb2(sfac,[3e4 0.5 0]); -# title('moment') diff --git a/examples/ex_tutorial_1.py b/examples/ex_tutorial_1.py deleted file mode 100644 index 66eb8e9..0000000 --- a/examples/ex_tutorial_1.py +++ /dev/null @@ -1,53 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Sat Mar 3 22:08:29 2018 - -@author: Jonas Lindemann -""" - -import calfem.geometry as cfg -import calfem.mesh as cfm -import calfem.vis_mpl as cfv - -# ----- Define geometry - -g = cfg.Geometry() - -g.point([0.0, 0.0]) # point 0 -g.point([5.0, 0.0], marker=20) # point 1 -g.point([2.5, 4.0]) # point 2 - -g.spline([0, 1]) # line 0 -g.spline([1, 2]) # line 1 -g.spline([2, 0], marker=10) # line 2 - -g.surface([0, 1, 2]) - -# ----- Create mesh - -mesh = cfm.GmshMesh(g) - -mesh.elType = 2 # Degrees of freedom per node. -mesh.dofsPerNode = 1 # Factor that changes element sizes. -mesh.elSizeFactor = 0.15 - -coords, edof, dofs, bdofs, elementmarkers = mesh.create() - -print(bdofs) - -cfv.draw_geometry(g) - -cfv.figure() - -# ----- Draw the mesh. - -cfv.draw_mesh( - coords=coords, - edof=edof, - dofs_per_node=mesh.dofsPerNode, - el_type=mesh.elType, - filled=True, - title="Example 01" - ) - -cfv.showAndWait() \ No newline at end of file diff --git a/examples/ex_tutorial_2.py b/examples/ex_tutorial_2.py deleted file mode 100644 index 233b2bd..0000000 --- a/examples/ex_tutorial_2.py +++ /dev/null @@ -1,113 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Sat Mar 3 22:08:29 2018 - -@author: Jonas Lindemann -""" - -import calfem.core as cfc -import calfem.geometry as cfg -import calfem.mesh as cfm -import calfem.vis_mpl as cfv -import calfem.utils as cfu - -import numpy as np -from math import * - -# ----- Problem parameters - -l = 5.0 -h = 1.0 -t = 0.2 - -v = 0.35 -E = 2.1e9 -ptype = 1 -ep = [ptype,t] -D=cfc.hooke(ptype, E, v) - -left_support = 10 -right_support = 20 -top_line = 30 - -# ----- Define geometry - -g = cfg.Geometry() - -g.point([0.0, 0.0], marker = left_support) # point 0 -g.point([l, 0.0], marker = right_support) # point 1 -g.point([l, h]) # point 2 -g.point([0.0, h]) # point 2 - -g.spline([0, 1]) # line 0 -g.spline([1, 2]) # line 1 -g.spline([2, 3], marker = top_line) # line 2 -g.spline([3, 0]) # line 2 - -g.surface([0, 1, 2, 3]) - -# ----- Create mesh - -mesh = cfm.GmshMesh(g) - -mesh.elType = 3 # Degrees of freedom per node. -mesh.dofsPerNode = 2 # Factor that changes element sizes. -mesh.elSizeFactor = 0.10 - -coords, edof, dofs, bdofs, elementmarkers = mesh.create() - -# ----- Solve problem - -nDofs = np.size(dofs) -ex, ey = cfc.coordxtr(edof, coords, dofs) - -K = np.zeros([nDofs,nDofs]) - -for eltopo, elx, ely in zip(edof, ex, ey): - Ke = cfc.planqe(elx, ely, ep, D) - cfc.assem(eltopo, K, Ke) - -bc = np.array([],'i') -bcVal = np.array([],'f') - -bc, bcVal = cfu.applybc(bdofs, bc, bcVal, left_support, 0.0, 0) -bc, bcVal = cfu.applybc(bdofs, bc, bcVal, right_support, 0.0, 2) - -f = np.zeros([nDofs,1]) - -cfu.applyforcetotal(bdofs, f, top_line, -10e5, 2) - -a,r = cfc.solveq(K,f,bc,bcVal) - -ed = cfc.extractEldisp(edof,a) -vonMises = [] - -for i in range(edof.shape[0]): - es, et = cfc.planqs(ex[i,:], ey[i,:], ep, D, ed[i,:]) - vonMises.append( sqrt( pow(es[0],2) - es[0]*es[1] + pow(es[1],2) + 3*es[2] ) ) - -# ----- Draw geometry - -cfv.draw_geometry(g) - -# ----- Draw the mesh. - -cfv.figure() -cfv.draw_mesh( - coords=coords, - edof=edof, - dofs_per_node=mesh.dofsPerNode, - el_type=mesh.elType, - filled=True, - title="Example 01" - ) - -# ----- Draw results - -cfv.figure() -cfv.draw_element_values(vonMises, coords, edof, mesh.dofs_per_node, mesh.el_type, a, draw_elements=True, draw_undisplaced_mesh=False, title="Example 06 effective stress") - -cfv.figure() -cfv.draw_displacements(a, coords, edof, mesh.dofs_per_node, mesh.el_type, draw_undisplaced_mesh=True, title="Example 06", magnfac=10.0) - -cfv.showAndWait() \ No newline at end of file diff --git a/examples/exd_beam2_b.py b/examples/exd_beam2_b.py index 71dac27..1516d7b 100644 --- a/examples/exd_beam2_b.py +++ b/examples/exd_beam2_b.py @@ -6,75 +6,77 @@ # # Note: file exd_beam2_m.py must be in the same directory # ---------------------------------------------------------------- + from exd_beam2_m import * # ----- Impact, center point, vertical beam ---------------------- + dt = 0.002 T = 1 # ----- Boundary condition, initial condition -------------------- -G = np.array([ - [0, 0], - [0.1, 0.02], - [0.2, -0.01], - [0.3, 0], - [T, 0] -]) -t,g = cfc.gfunc(G,dt) +G = np.array([[0, 0], [0.1, 0.02], [0.2, -0.01], [0.3, 0], [T, 0]]) -bc = np.zeros((4, 1+len(g))) -bc[0,:] = np.hstack((1,g)) -bc[1,0] = 2 -bc[2,0] = 3 -bc[3,0] = 14 +t, g = cfc.gfunc(G, dt) -a0 = np.zeros((15,1)) -da0 = np.zeros((15,1)) +bc = np.zeros((4, 1 + len(g))) +bc[0, :] = np.hstack((1, g)) +bc[1, 0] = 2 +bc[2, 0] = 3 +bc[3, 0] = 14 + +a0 = np.zeros((15, 1)) +da0 = np.zeros((15, 1)) # ----- Output parameters ---------------------------------------- -times = np.arange(0.1,1.1,0.1) -dofs = np.array([1,4,11]) + +times = np.arange(0.1, 1.1, 0.1) +dofs = np.array([1, 4, 11]) # ----- Time integration parameters ------------------------------ + ip = np.array([dt, T, 0.25, 0.5]) # ----- Time integration ----------------------------------------- -sol, dofhist = cfc.step2(K,[],M,[],a0,da0,bc,ip,times,dofs) + +sol, dofhist = cfc.step2(K, [], M, [], a0, da0, bc, ip, times, dofs) # ----- Plot time history for two DOFs --------------------------- -cfv.figure(1,fig_size=(7,4)) -cfv.plt.plot(t,dofhist['a'][0,:], '-') -cfv.plt.plot(t,dofhist['a'][1,:],'--') -cfv.plt.plot(t,dofhist['a'][2,:],'-.') -cfv.plt.xlim([0,1]) -cfv.plt.ylim([-0.02,0.03]) + +cfv.figure(1, fig_size=(7, 4)) +cfv.plt.plot(t, dofhist["a"][0, :], "-") +cfv.plt.plot(t, dofhist["a"][1, :], "--") +cfv.plt.plot(t, dofhist["a"][2, :], "-.") +cfv.plt.xlim([0, 1]) +cfv.plt.ylim([-0.02, 0.03]) cfv.plt.xlabel("time (sec)") cfv.plt.ylabel("displacement (m)") cfv.plt.title("Displacement(time) at the 1st, 4th and 11th degree of freedom") -cfv.text("solid line = bottom, vertical beam, x-direction", [0.2,0.022]) -cfv.text("dashed line = center, vertical beam, x-direction", [0.2,0.017]) -cfv.text("dashed-dotted line = center, horizontal beam, y-direction", [0.2,0.012]) +cfv.text("solid line = bottom, vertical beam, x-direction", [0.2, 0.022]) +cfv.text("dashed line = center, vertical beam, x-direction", [0.2, 0.017]) +cfv.text("dashed-dotted line = center, horizontal beam, y-direction", [0.2, 0.012]) cfv.plt.grid() # ----- Plot displacement for some time increments ---------------- -cfv.figure(2,fig_size=(7,5)) + +cfv.figure(2, fig_size=(7, 5)) for i in range(5): - Edb = cfc.extract_ed(edof,sol['a'][:,i]) - ext = ex+i*3.5 - cfv.eldraw2(ext,ey,[2,3,1]) - cfv.eldisp2(ext,ey,Edb,[1,2,2],sfac=20) - cfv.text(f"{times[i]:.1f}", [3.5*i+0.5,1.5]) -eyt = ey-4 -for i in range(5,10): - Edb = cfc.extract_ed(edof,sol['a'][:,i]) - ext = ex+(i-5)*3.5 - cfv.eldraw2(ext,eyt,[2,3,1]) - cfv.eldisp2(ext,eyt,Edb,[1,2,2],sfac=20) - cfv.text(f"{times[i]:.1f}", [3.5*(i-5)+0.5,-2.5]) + Edb = cfc.extract_ed(edof, sol["a"][:, i]) + ext = ex + i * 3.5 + cfv.eldraw2(ext, ey, [2, 3, 1]) + cfv.eldisp2(ext, ey, Edb, [1, 2, 2], sfac=20) + cfv.text(f"{times[i]:.1f}", [3.5 * i + 0.5, 1.5]) +eyt = ey - 4 +for i in range(5, 10): + Edb = cfc.extract_ed(edof, sol["a"][:, i]) + ext = ex + (i - 5) * 3.5 + cfv.eldraw2(ext, eyt, [2, 3, 1]) + cfv.eldisp2(ext, eyt, Edb, [1, 2, 2], sfac=20) + cfv.text(f"{times[i]:.1f}", [3.5 * (i - 5) + 0.5, -2.5]) cfv.title("Snapshots (sec), magnification = 20") ax = cfv.gca() ax.set_axis_off() -cfv.showAndWait() +cfv.show_and_wait() -# ----- End ------------------------------------------------------- \ No newline at end of file +# ----- End ------------------------------------------------------- diff --git a/examples/exd_beam2_m.py b/examples/exd_beam2_m.py index 52fb35a..0b53226 100644 --- a/examples/exd_beam2_m.py +++ b/examples/exd_beam2_m.py @@ -5,53 +5,63 @@ # for a simple frame structure. # ---------------------------------------------------------------- - import numpy as np import calfem.core as cfc import calfem.vis_mpl as cfv # ----- Generate the model --------------------------------------- # ----- Material data -------------------------------------------- + E = 3e10 -Av=0.1030e-2 -Ah=0.0764e-2 -rho=2500 +Av = 0.1030e-2 +Ah = 0.0764e-2 +rho = 2500 Iv = 0.0171e-4 -Ih=0.00801e-4 -ep1 = [E, Av, Iv, rho*Av] #IPE100 -ep2 = [E, Ah, Ih, rho*Ah] #IPE80 +Ih = 0.00801e-4 +ep1 = [E, Av, Iv, rho * Av] # IPE100 +ep2 = [E, Ah, Ih, rho * Ah] # IPE80 # ----- Topology ------------------------------------------------- -edof = np.array([ - [1,2,3,4,5,6], - [4,5,6,7,8,9], - [7,8,9,10,11,12], - [10,11,12,13,14,15] -]) + +edof = np.array( + [ + [1, 2, 3, 4, 5, 6], + [4, 5, 6, 7, 8, 9], + [7, 8, 9, 10, 11, 12], + [10, 11, 12, 13, 14, 15], + ] +) # ----- List of coordinates -------------------------------------- -coord = np.array([ - [0.0, 0.0], - [0.0, 1.5], - [0.0, 3.0], - [1.0, 3.0], - [2.0, 3.0], -]) + +coord = np.array( + [ + [0.0, 0.0], + [0.0, 1.5], + [0.0, 3.0], + [1.0, 3.0], + [2.0, 3.0], + ] +) # ----- List of degrees of freedom ------------------------------- -dof = np.array([ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - [10, 11, 12], - [13, 14, 15], -]) + +dof = np.array( + [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], + [10, 11, 12], + [13, 14, 15], + ] +) # ----- Generate element matrices, assemble in global matrices --- -K = np.zeros([15,15]) -M = np.zeros([15,15]) -ex, ey = cfc.coordxtr(edof, coord, dof); +K = np.zeros([15, 15]) +M = np.zeros([15, 15]) + +ex, ey = cfc.coordxtr(edof, coord, dof) ep = np.array([ep1, ep1, ep2, ep2]) for elx, ely, eltopo, elprop in zip(ex, ey, edof, ep): @@ -60,45 +70,48 @@ cfc.assem(eltopo, M, Me) # ----- Eigenvalue analysis -------------------------------------- + b = np.array([1, 2, 3, 14]) -La, Egv = cfc.eigen(K,M,b) -freq = np.sqrt(La)/(2*np.pi) +La, Egv = cfc.eigen(K, M, b) +freq = np.sqrt(La) / (2 * np.pi) -if __name__=='__main__': +if __name__ == "__main__": # ----- Draw a plot of the element mesh -------------------------- - cfv.figure(1,fig_size=(5.5, 4.5)) - cfv.eldraw2(ex,ey,[1, 2, 1]) - cfv.title('2-D Frame Structure') + + cfv.figure(1, fig_size=(5.5, 4.5)) + cfv.eldraw2(ex, ey, [1, 2, 1]) + cfv.title("2-D Frame Structure") # ----- Plot one eigenmode --------------------------------------- - cfv.figure(2,fig_size=(5.5, 4.5)) - cfv.eldraw2(ex,ey,[2, 3, 1]) - Edb = cfc.extract_ed(edof, Egv[:,0]) - cfv.eldisp2(ex,ey,Edb,[1, 2, 2]) - cfv.title('The first eigenmode') - cfv.text(f"{freq[0]:.2f}", [0.5,1.75]) + + cfv.figure(2, fig_size=(5.5, 4.5)) + cfv.eldraw2(ex, ey, [2, 3, 1]) + Edb = cfc.extract_ed(edof, Egv[:, 0]) + cfv.eldisp2(ex, ey, Edb, [1, 2, 2]) + cfv.title("The first eigenmode") + cfv.text(f"{freq[0]:.2f}", [0.5, 1.75]) ax = cfv.gca() ax.grid() # ----- Plot eight eigenmodes ------------------------------------ - cfv.figure(3,fig_size=(7,5)) + + cfv.figure(3, fig_size=(7, 5)) for i in range(4): - Edb = cfc.extract_ed(edof,Egv[:,i]) - ext = ex+i*3 - cfv.eldraw2(ext,ey,[2,3,1]) - cfv.eldisp2(ext,ey,Edb,[1,2,2],sfac=0.5) - cfv.text(f"{freq[i]:.2f}", [3*i+0.5,1.5]) - eyt = ey-4 - for i in range(4,8): - Edb = cfc.extract_ed(edof,Egv[:,i]) - ext = ex+(i-4)*3 - cfv.eldraw2(ext,eyt,[2,3,1]) - cfv.eldisp2(ext,eyt,Edb,[1,2,2],sfac=0.5) - cfv.text(f"{freq[i]:.2f}", [3*(i-4)+0.5,-2.5]) + Edb = cfc.extract_ed(edof, Egv[:, i]) + ext = ex + i * 3 + cfv.eldraw2(ext, ey, [2, 3, 1]) + cfv.eldisp2(ext, ey, Edb, [1, 2, 2], sfac=0.5) + cfv.text(f"{freq[i]:.2f}", [3 * i + 0.5, 1.5]) + eyt = ey - 4 + for i in range(4, 8): + Edb = cfc.extract_ed(edof, Egv[:, i]) + ext = ex + (i - 4) * 3 + cfv.eldraw2(ext, eyt, [2, 3, 1]) + cfv.eldisp2(ext, eyt, Edb, [1, 2, 2], sfac=0.5) + cfv.text(f"{freq[i]:.2f}", [3 * (i - 4) + 0.5, -2.5]) cfv.title("The first eight eigenmodes [Hz]") ax = cfv.gca() ax.set_axis_off() - cfv.showAndWait() + cfv.show_and_wait() # ----- End ------------------------------------------------------- - diff --git a/examples/exd_beam2_t.py b/examples/exd_beam2_t.py index 6bac640..c36d902 100644 --- a/examples/exd_beam2_t.py +++ b/examples/exd_beam2_t.py @@ -5,76 +5,75 @@ # # Note: file exd_beam2_m.py must be in the same directory # ---------------------------------------------------------------- + from exd_beam2_m import * # ----- Impact, center point, vertical beam ---------------------- + dt = 0.002 T = 1 # ----- The load ------------------------------------------------- -G = np.array([ - [0, 0], - [0.15, 1], - [0.25, 0], - [T, 0] -]) - -t,g = cfc.gfunc(G,dt) + +G = np.array([[0, 0], [0.15, 1], [0.25, 0], [T, 0]]) + +t, g = cfc.gfunc(G, dt) f = np.zeros((15, len(g))) -f[3,:] = 1000*g +f[3, :] = 1000 * g # ----- Boundary condition, initial condition -------------------- -bc = np.array([ - [1, 0], - [2, 0], - [3, 0], - [14, 0] -]) -a0 = np.zeros((15,1)) -da0 = np.zeros((15,1)) +bc = np.array([[1, 0], [2, 0], [3, 0], [14, 0]]) + +a0 = np.zeros((15, 1)) +da0 = np.zeros((15, 1)) # ----- Output parameters ---------------------------------------- -times = np.arange(0.1,1.1,0.1) -dofs = np.array([4,11]) + +times = np.arange(0.1, 1.1, 0.1) +dofs = np.array([4, 11]) # ----- Time integration parameters ------------------------------ + ip = np.array([dt, T, 0.25, 0.5]) # ----- Time integration ----------------------------------------- -sol, dofhist = cfc.step2(K,[],M,f,a0,da0,bc,ip,times,dofs) + +sol, dofhist = cfc.step2(K, [], M, f, a0, da0, bc, ip, times, dofs) # ----- Plot time history for two DOFs --------------------------- -cfv.figure(1,fig_size=(7,4)) -cfv.plt.plot(t,dofhist['a'][0,:], '-') -cfv.plt.plot(t,dofhist['a'][1,:],'--') -cfv.plt.xlim([0,1]) -cfv.plt.ylim([-0.01,0.02]) + +cfv.figure(1, fig_size=(7, 4)) +cfv.plt.plot(t, dofhist["a"][0, :], "-") +cfv.plt.plot(t, dofhist["a"][1, :], "--") +cfv.plt.xlim([0, 1]) +cfv.plt.ylim([-0.01, 0.02]) cfv.plt.xlabel("time (sec)") cfv.plt.ylabel("displacement (m)") cfv.plt.title("Displacement(time) at the 4th and 11th degree of freedom") -cfv.text("solid line = impact point, x-direction", [0.3,0.017]) -cfv.text("dashed line = center, horizontal beam, y-direction", [0.3,0.012]) +cfv.text("solid line = impact point, x-direction", [0.3, 0.017]) +cfv.text("dashed line = center, horizontal beam, y-direction", [0.3, 0.012]) cfv.plt.grid() # ----- Plot displacement for some time increments ---------------- -cfv.figure(2,fig_size=(7,5)) + +cfv.figure(2, fig_size=(7, 5)) for i in range(5): - Edb = cfc.extract_ed(edof,sol['a'][:,i]) - ext = ex+i*3.5 - cfv.eldraw2(ext,ey,[2,3,1]) - cfv.eldisp2(ext,ey,Edb,[1,2,2],sfac=25) - cfv.text(f"{times[i]:.1f}", [3.5*i+0.5,1.5]) -eyt = ey-4 -for i in range(5,10): - Edb = cfc.extract_ed(edof,sol['a'][:,i]) - ext = ex+(i-5)*3.5 - cfv.eldraw2(ext,eyt,[2,3,1]) - cfv.eldisp2(ext,eyt,Edb,[1,2,2],sfac=25) - cfv.text(f"{times[i]:.1f}", [3.5*(i-5)+0.5,-2.5]) + Edb = cfc.extract_ed(edof, sol["a"][:, i]) + ext = ex + i * 3.5 + cfv.eldraw2(ext, ey, [2, 3, 1]) + cfv.eldisp2(ext, ey, Edb, [1, 2, 2], sfac=25) + cfv.text(f"{times[i]:.1f}", [3.5 * i + 0.5, 1.5]) +eyt = ey - 4 +for i in range(5, 10): + Edb = cfc.extract_ed(edof, sol["a"][:, i]) + ext = ex + (i - 5) * 3.5 + cfv.eldraw2(ext, eyt, [2, 3, 1]) + cfv.eldisp2(ext, eyt, Edb, [1, 2, 2], sfac=25) + cfv.text(f"{times[i]:.1f}", [3.5 * (i - 5) + 0.5, -2.5]) cfv.title("Snapshots (sec), magnification = 25") ax = cfv.gca() ax.set_axis_off() -cfv.showAndWait() +cfv.show_and_wait() -# ----- End ------------------------------------------------------- \ No newline at end of file +# ----- End ------------------------------------------------------- diff --git a/examples/exd_beam2_tr.py b/examples/exd_beam2_tr.py index 0dd929f..685891e 100644 --- a/examples/exd_beam2_tr.py +++ b/examples/exd_beam2_tr.py @@ -5,81 +5,90 @@ # # Note: file exd_beam2_m.py must be in the same directory # ---------------------------------------------------------------- + from exd_beam2_m import * # ----- Impact, center point, vertical beam ---------------------- + dt = 0.002 T = 1 nev = 2 # ----- The load ------------------------------------------------- -G = np.array([ - [0, 0], - [0.15, 1], - [0.25, 0], - [T, 0] -]) - -t,g = cfc.gfunc(G,dt) + +G = np.array([[0, 0], [0.15, 1], [0.25, 0], [T, 0]]) + +t, g = cfc.gfunc(G, dt) f = np.zeros((15, len(g))) -f[3,:] = 1000*g -fr = np.hstack((np.arange(1,nev+1).reshape(-1,1),Egv[:,:nev].T@f)) +f[3, :] = 1000 * g +fr = np.hstack((np.arange(1, nev + 1).reshape(-1, 1), Egv[:, :nev].T @ f)) # ----- Reduced system matrices ---------------------------------- -kr = np.diag(np.diag(Egv[:,:nev].T@K@Egv[:,:nev])) -mr = np.diag(np.diag(Egv[:,:nev].T@M@Egv[:,:nev])) + +kr = np.diag(np.diag(Egv[:, :nev].T @ K @ Egv[:, :nev])) +mr = np.diag(np.diag(Egv[:, :nev].T @ M @ Egv[:, :nev])) # ----- Initial condition ---------------------------------------- -ar0 = np.zeros((nev,1)) -dar0 = np.zeros((nev,1)) + +ar0 = np.zeros((nev, 1)) +dar0 = np.zeros((nev, 1)) # ----- Output parameters ---------------------------------------- -times = np.arange(0.1,1.1,0.1) -dofsr = np.arange(1,nev+1) -dofs = np.array([4,11]) + +times = np.arange(0.1, 1.1, 0.1) +dofsr = np.arange(1, nev + 1) +dofs = np.array([4, 11]) # ----- Time integration parameters ------------------------------ + ip = np.array([dt, T, 0.25, 0.5]) # ----- Time integration ----------------------------------------- -sol, dofhist = cfc.step2(kr,[],mr,fr,ar0,dar0,[],ip,times,dofsr) +sol, dofhist = cfc.step2(kr, [], mr, fr, ar0, dar0, [], ip, times, dofsr) # ----- Mapping back to original coordinate system --------------- -aR = Egv[:,:nev]@sol['a'] -aRhist = Egv[dofs-1,:nev]@dofhist['a'] + +aR = Egv[:, :nev] @ sol["a"] +aRhist = Egv[dofs - 1, :nev] @ dofhist["a"] # ----- Plot time history for two DOFs --------------------------- -cfv.figure(1,fig_size=(7,4)) -cfv.plt.plot(t,aRhist[0,:], '-') -cfv.plt.plot(t,aRhist[1,:],'--') -cfv.plt.xlim([0,1]) -cfv.plt.ylim([-0.01,0.02]) + +cfv.figure(1, fig_size=(7, 4)) +cfv.plt.plot(t, aRhist[0, :], "-") +cfv.plt.plot(t, aRhist[1, :], "--") +cfv.plt.xlim([0, 1]) +cfv.plt.ylim([-0.01, 0.02]) cfv.plt.xlabel("time (sec)") cfv.plt.ylabel("displacement (m)") cfv.plt.title("Displacement(time) at the 4th and 11th degree of freedom") -cfv.text("solid line = impact point, x-direction", [0.3,0.017]) -cfv.text("dashed line = center, horizontal beam, y-direction", [0.3,0.012]) -cfv.text("TWO EIGENVECTORS ARE USED", [0.3,-0.007]) +cfv.text("solid line = impact point, x-direction", [0.3, 0.017]) +cfv.text("dashed line = center, horizontal beam, y-direction", [0.3, 0.012]) +cfv.text("TWO EIGENVECTORS ARE USED", [0.3, -0.007]) cfv.plt.grid() # ----- Plot displacement for some time increments ---------------- -cfv.figure(2,fig_size=(7,5)) + +cfv.figure(2, fig_size=(7, 5)) + for i in range(5): - Edb = cfc.extract_ed(edof,aR[:,i]) - ext = ex+i*3.5 - cfv.eldraw2(ext,ey,[2,3,1]) - cfv.eldisp2(ext,ey,Edb,[1,2,2],sfac=25) - cfv.text(f"{times[i]:.1f}", [3.5*i+0.5,1.5]) -eyt = ey-4 -for i in range(5,10): - Edb = cfc.extract_ed(edof,aR[:,i]) - ext = ex+(i-5)*3.5 - cfv.eldraw2(ext,eyt,[2,3,1]) - cfv.eldisp2(ext,eyt,Edb,[1,2,2],sfac=25) - cfv.text(f"{times[i]:.1f}", [3.5*(i-5)+0.5,-2.5]) + Edb = cfc.extract_ed(edof, aR[:, i]) + ext = ex + i * 3.5 + cfv.eldraw2(ext, ey, [2, 3, 1]) + cfv.eldisp2(ext, ey, Edb, [1, 2, 2], sfac=25) + cfv.text(f"{times[i]:.1f}", [3.5 * i + 0.5, 1.5]) + +eyt = ey - 4 + +for i in range(5, 10): + Edb = cfc.extract_ed(edof, aR[:, i]) + ext = ex + (i - 5) * 3.5 + cfv.eldraw2(ext, eyt, [2, 3, 1]) + cfv.eldisp2(ext, eyt, Edb, [1, 2, 2], sfac=25) + cfv.text(f"{times[i]:.1f}", [3.5 * (i - 5) + 0.5, -2.5]) + cfv.title("Snapshots (sec), magnification = 25") ax = cfv.gca() ax.set_axis_off() -cfv.showAndWait() +cfv.show_and_wait() -# ----- End ------------------------------------------------------- \ No newline at end of file +# ----- End ------------------------------------------------------- diff --git a/examples/exe_stress_2d_editor.py b/examples/exe_stress_2d_editor.py index c84bf87..e5aed51 100644 --- a/examples/exe_stress_2d_editor.py +++ b/examples/exe_stress_2d_editor.py @@ -18,15 +18,15 @@ g = cfg.Geometry() -g.point([0.0, 0.0]) # point 0 +g.point([0.0, 0.0]) # point 0 g.point([100.0, 0.0]) # point 1 -g.point([100, 100]) # point 2 -g.point([0, 100]) # point 3 +g.point([100, 100]) # point 2 +g.point([0, 100]) # point 3 -g.spline([0, 1]) # line 0 -g.spline([1, 2]) # line 1 -g.spline([2, 3]) # line 2 -g.spline([3, 0]) # line 3 +g.spline([0, 1]) # line 0 +g.spline([1, 2]) # line 1 +g.spline([2, 3]) # line 2 +g.spline([3, 0]) # line 3 g.surface([0, 1, 2, 3]) # Connect lines to form surface g.setCurveMarker(0, 10) @@ -41,33 +41,34 @@ v = 0.35 E = 2.1e9 ptype = 1 -ep = [ptype,t] +ep = [ptype, t] D = cfc.hooke(ptype, E, v) -# --- Every border or point marked with 10 will recieve boundary +# --- Every border or point marked with 10 will recieve boundary # --- condition value of 0 -bcs_new = [[marker_dict[10], 0]] +bcs_new = [[marker_dict[10], 0]] -# --- Every border or point marked with 20 will recieve load +# --- Every border or point marked with 20 will recieve load # --- value of 10e5 -loads_new = [[marker_dict[20], 10e5]] +loads_new = [[marker_dict[20], 10e5]] -# --- Every border or point marked with A will recieve boundary +# --- Every border or point marked with A will recieve boundary # --- condition value of 0 -bcs_old = [[10, 0]] +bcs_old = [[10, 0]] -# --- Every border or point marked with B will recieve load +# --- Every border or point marked with B will recieve load # --- value of 10e5 -loads_old = [[20, 10e5]] +loads_old = [[20, 10e5]] el_size_factor = 5 el_type = 3 dofs_per_node = 2 + def calc(geometry, bcs, loads, text): mesh = cfm.GmshMeshGenerator(geometry) mesh.el_size_factor = el_size_factor # Factor that changes element sizes. @@ -89,7 +90,7 @@ def calc(geometry, bcs, loads, text): for eltopo, elx, ely in zip(edof, ex, ey): Ke = cfc.planqe(elx, ely, ep, D) cfc.assem(eltopo, K, Ke) - + # --- Solve equation system f = np.zeros([nDofs, 1]) @@ -113,16 +114,28 @@ def calc(geometry, bcs, loads, text): # --- For each element: for i in range(edof.shape[0]): - # --- Determine element stresses and strains in the element. - es, et = cfc.planqs(ex[i,:], ey[i,:], ep, D, ed[i,:]) + es, et = cfc.planqs(ex[i, :], ey[i, :], ep, D, ed[i, :]) # --- Calc and append effective stress to list. - vonMises.append(np.sqrt(np.power(es[0],2) - es[0]*es[1] + np.power(es[1],2) + 3*es[2] ) ) + vonMises.append( + np.sqrt(np.power(es[0], 2) - es[0] * es[1] + np.power(es[1], 2) + 3 * es[2]) + ) title = "Effective stress" + text - cfv.draw_element_values(vonMises, coords, edof, mesh.dofs_per_node, mesh.el_type, None, draw_elements=False, draw_undisplaced_mesh=False, title=title) + cfv.draw_element_values( + vonMises, + coords, + edof, + mesh.dofs_per_node, + mesh.el_type, + None, + draw_elements=False, + draw_undisplaced_mesh=False, + title=title, + ) + # --- Display results @@ -131,4 +144,3 @@ def calc(geometry, bcs, loads, text): cfv.figure() calc(new_geometry, bcs_new, loads_new, " modified") cfv.show_and_wait() - diff --git a/examples/exm_circle_bsplines.py b/examples/exm_circle_bsplines.py index 4b04bd0..fff750c 100644 --- a/examples/exm_circle_bsplines.py +++ b/examples/exm_circle_bsplines.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Example 02 Creating geometry from B-Splines and circle arcs. Also shows how to set ID numbers for geometry entities and how to specify element density. -''' +""" import matplotlib.pyplot as plt import matplotlib.collections @@ -21,16 +21,16 @@ # Add points: # In this example we set the IDs manually. -g.point([-2, 0], ID=0) +g.point([-2, 0], ID=0) # el_size determines the size of the elements near this point. -g.point([0, 1], ID=1, el_size=5) +g.point([0, 1], ID=1, el_size=5) # el_size is 1 by default. Larger number means less dense mesh. -g.point([1, 0], 2, el_size=5) +g.point([1, 0], 2, el_size=5) g.point([0, -2], 3) # Size means the length of the sides of the elements. -g.point([0, 0], 4, el_size=5) -g.point([.5, .2], 5) -g.point([-.5, .5], 6) -g.point([-.7, -.5], 7) +g.point([0, 0], 4, el_size=5) +g.point([0.5, 0.2], 5) +g.point([-0.5, 0.5], 6) +g.point([-0.7, -0.5], 7) # Add curves: @@ -78,25 +78,19 @@ # Draw the geometry. -cfv.draw_geometry( - g, - label_curves=True, - title="Example 2 - Geometry" -) - -# New figure window - cfv.figure() +cfv.draw_geometry(g, label_curves=True, title="Example 2 - Geometry") # Draws the mesh. +cfv.figure() cfv.draw_mesh( coords=coords, edof=edof, dofs_per_node=mesh.dofs_per_node, el_type=mesh.el_type, filled=True, - title="Example 2 - Mesh" + title="Example 2 - Mesh", ) # Enter main loop diff --git a/examples/exm_flow_model.py b/examples/exm_flow_model.py index 76fb6d7..c11c251 100644 --- a/examples/exm_flow_model.py +++ b/examples/exm_flow_model.py @@ -8,7 +8,7 @@ # ---------------------------------------------------------------- # # REFERENCES -# J Lindemann 2021-12-29 +# J Lindemann 2021-12-29 # ---------------------------------------------------------------- # ----- Import needed modules ------------------------------------ @@ -25,23 +25,23 @@ w = 100.0 h = 10.0 t = 1.0 -d = h/2 +d = h / 2 -D = np.identity(2, 'float') +D = np.identity(2, "float") ep = [1.0, 1] # ----- Create geometry object ----------------------------------- g = cfg.Geometry() -g.point([0, 0]) # point 1 -g.point([w, 0]) # point 2 -g.point([w, h]) # point 3 -g.point([w-w/2+t/2, h]) # point 4 -g.point([w-w/2+t/2, h-d]) # point 5 -g.point([w-w/2-t/2, h-d]) # point 6 -g.point([w-w/2-t/2, h]) # point 7 -g.point([0, h]) # point 8 +g.point([0, 0]) # point 1 +g.point([w, 0]) # point 2 +g.point([w, h]) # point 3 +g.point([w - w / 2 + t / 2, h]) # point 4 +g.point([w - w / 2 + t / 2, h - d]) # point 5 +g.point([w - w / 2 - t / 2, h - d]) # point 6 +g.point([w - w / 2 - t / 2, h]) # point 7 +g.point([0, h]) # point 8 # ----- Create lines between points ------------------------------ @@ -50,7 +50,7 @@ g.spline([0, 1]) g.spline([1, 2]) -g.spline([2, 3], marker=left_side) # marker just to name +g.spline([2, 3], marker=left_side) # marker just to name g.spline([3, 4]) g.spline([4, 5]) g.spline([5, 6]) @@ -63,8 +63,8 @@ # ----- Mesh generation ------------------------------------------ -el_type = 3 # quadrature element -dofs_per_node = 1 # 1 dof +el_type = 3 # quadrature element +dofs_per_node = 1 # 1 dof # ----- Set mesh paramters --------------------------------------- @@ -83,13 +83,13 @@ ex, ey = cfc.coordxtr(edof, coords, dofs) K = np.zeros([nDofs, nDofs]) -for eltopo, elx, ely, in zip(edof, ex, ey): +for eltopo, elx, ely in zip(edof, ex, ey): Ke = cfc.flw2i4e(elx, ely, ep, D) cfc.assem(eltopo, K, Ke) # ----- Force vector --------------------------------------------- -f = np.zeros([nDofs, 1]) +f = np.zeros([nDofs, 1]) # ----- Boundary conditions -------------------------------------- @@ -106,7 +106,7 @@ # ----- Calculating element forces ------------------------------- -maxFlow = [] # empty list to store flow +maxFlow = [] # empty list to store flow for i in range(edof.shape[0]): es, et, eci = cfc.flw2i4s(ex[i, :], ey[i, :], ep, D, ed[i, :]) @@ -115,15 +115,14 @@ # ----- Visualize results ---------------------------------------- cfv.figure() -cfv.draw_geometry(g, title='Geometry') +cfv.draw_geometry(g, title="Geometry") cfv.figure() -cfv.draw_element_values(maxFlow, coords, edof, dofs_per_node, el_type, None, - title='Max flows') +cfv.draw_element_values( + maxFlow, coords, edof, dofs_per_node, el_type, None, title="Max flows" +) cfv.figure() -cfv.draw_nodal_values(a, coords, edof, - dofs_per_node=dofs_per_node, - el_type=el_type) +cfv.draw_nodal_values(a, coords, edof, dofs_per_node=dofs_per_node, el_type=el_type) cfv.showAndWait() diff --git a/examples/exm_geometry.py b/examples/exm_geometry.py index 9c37d8c..0a92cb9 100644 --- a/examples/exm_geometry.py +++ b/examples/exm_geometry.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -'''Example 01 +"""Example 01 Shows how to create simple geometry from splines and ellipse arcs, and how to mesh a quad mesh in GmshMesher. Also demonstrates drawGeometry(), drawMesh, and drawing texts and labels in a figure. -''' +""" import calfem.geometry as cfg import calfem.mesh as cfm @@ -48,15 +48,15 @@ g.ellipse([7, 8, 9, 10], marker=50) -g.spline([0, 1], marker=80) # 1 - A spline. Splines pass through the - # points in the first parameter. -g.spline([2, 1]) # 2 -g.spline([3, 2]) # 3 -g.spline([0, 3]) # 4 -g.spline([7, 9], marker=50) # 5 -g.spline([10, 9]) # 6 -g.spline([4, 5, 6, 4]) # 7 - This is a closed spline. - # The start and end points are the same +g.spline([0, 1], marker=80) # 1 - A spline. Splines pass through the +# points in the first parameter. +g.spline([2, 1]) # 2 +g.spline([3, 2]) # 3 +g.spline([0, 3]) # 4 +g.spline([7, 9], marker=50) # 5 +g.spline([10, 9]) # 6 +g.spline([4, 5, 6, 4]) # 7 - This is a closed spline. +# The start and end points are the same # Add a surface: # Surfaces are defined by its curve boundaries. @@ -101,21 +101,19 @@ # Draw the geometry. Note that surfaces and volumes are not drawn at all by # this function. -cfv.draw_geometry(g) - -# New figure window - cfv.figure() +cfv.draw_geometry(g) # Draw the mesh. +cfv.figure() cfv.draw_mesh( coords=coords, edof=edof, dofs_per_node=mesh.dofs_per_node, el_type=mesh.el_type, filled=True, - title="Example 01" + title="Example 01", ) # Adds a text in world space @@ -129,7 +127,7 @@ # We can change the attributes of labels and texts, such as color, text, and position. our_label.set_text("Label, changed.") -our_label.set_color('r') # Make it red. (1,0,0) would also have worked. +our_label.set_color("r") # Make it red. (1,0,0) would also have worked. our_label.set_position((1, 0.3)) # Enter main loop: diff --git a/examples/exm_stress_2d.py b/examples/exm_stress_2d.py index 438be0f..d1bd19a 100644 --- a/examples/exm_stress_2d.py +++ b/examples/exm_stress_2d.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -'''Example 06 +"""Example 06 Solves a plane stress 2D problem using a structured mesh. Shows how to draw von Mises effective stress as an element value with drawElementValues(). Shows use of GmshMesher attribute 'nodesOnCurve' (dictionary that says which nodes are on a given geometry curve) -''' +""" import calfem.geometry as cfg import calfem.mesh as cfm @@ -36,37 +36,89 @@ # Just a shorthand. We use this to make the circle arcs. -s2 = 1/sqrt(2) - -points = [[0, 3], [2.5, 3], [3, 3], [4-s2, 3-s2], [4, 2], # 0-4 - [4+s2, 3-s2], [5, 3], [5.5, 3], [8, 3], [0, 1.5], # 5-9 - [2.5, 1.5], [4, 1.5], [5.5, 1.5], [8, 1.5], [0, 0], # 10-14 - [2.5, 0], [3, 0], [4-s2, s2], [4, 1], [4+s2, s2], # 15-19 - [5, 0], [5.5, 0], [8, 0], [4, 3], [4, 0]] # 20-24 +s2 = 1 / sqrt(2) + +points = [ + [0, 3], + [2.5, 3], + [3, 3], + [4 - s2, 3 - s2], + [4, 2], # 0-4 + [4 + s2, 3 - s2], + [5, 3], + [5.5, 3], + [8, 3], + [0, 1.5], # 5-9 + [2.5, 1.5], + [4, 1.5], + [5.5, 1.5], + [8, 1.5], + [0, 0], # 10-14 + [2.5, 0], + [3, 0], + [4 - s2, s2], + [4, 1], + [4 + s2, s2], # 15-19 + [5, 0], + [5.5, 0], + [8, 0], + [4, 3], + [4, 0], +] # 20-24 for xp, yp in points: - g.point([xp*0.1, yp*0.1]) - -splines = [[0, 1], [1, 2], [6, 7], [7, 8], [8, 13], # 0-4 - [13, 22], [22, 21], [21, 20], [16, 15], [15, 14], # 5-9 - [14, 9], [9, 0], [9, 10], [10, 1], [10, 15], # 10-14 - [10, 11], [11, 4], [11, 18], [11, 12], [12, 7], # 15-19 - [12, 21], [12, 13], [3, 10], [5, 12], [10, 17], # 20-24 - [12, 19]] # 25 + g.point([xp * 0.1, yp * 0.1]) + +splines = [ + [0, 1], + [1, 2], + [6, 7], + [7, 8], + [8, 13], # 0-4 + [13, 22], + [22, 21], + [21, 20], + [16, 15], + [15, 14], # 5-9 + [14, 9], + [9, 0], + [9, 10], + [10, 1], + [10, 15], # 10-14 + [10, 11], + [11, 4], + [11, 18], + [11, 12], + [12, 7], # 15-19 + [12, 21], + [12, 13], + [3, 10], + [5, 12], + [10, 17], # 20-24 + [12, 19], +] # 25 for s in splines: g.spline(s, el_on_curve=10) -g.curveMarker(ID=4, marker=7) # Assign marker 7 to the splines on the right. -g.curveMarker(ID=5, marker=7) # We will apply a force on nodes with marker 7. +g.curveMarker(ID=4, marker=7) # Assign marker 7 to the splines on the right. +g.curveMarker(ID=5, marker=7) # We will apply a force on nodes with marker 7. g.curveMarker(ID=10, marker=5) # Assign marker 5 to the splines on the left. # The nodes with marker 5 will be locked in place. g.curveMarker(ID=11, marker=5) # Points in circle arcs are [start, center, end] -circlearcs = [[2, 23, 3], [3, 23, 4], [4, 23, 5], [5, 23, 6], # 26-29 - [16, 24, 17], [17, 24, 18], [18, 24, 19], [19, 24, 20]] # 30-33 +circlearcs = [ + [2, 23, 3], + [3, 23, 4], + [4, 23, 5], + [5, 23, 6], # 26-29 + [16, 24, 17], + [17, 24, 18], + [18, 24, 19], + [19, 24, 20], +] # 30-33 for c in circlearcs: g.circle(c, el_on_curve=10) @@ -112,8 +164,8 @@ f = np.zeros([nDofs, 1]) -bc = np.array([], 'i') -bcVal = np.array([], 'f') +bc = np.array([], "i") +bcVal = np.array([], "f") bc, bcVal = cfu.applybc(bdofs, bc, bcVal, 5, 0.0, 0) @@ -129,15 +181,13 @@ # For each element: for i in range(edof.shape[0]): - # Determine element stresses and strains in the element. es, et = cfc.planqs(ex[i, :], ey[i, :], ep, D, ed[i, :]) # Calc and append effective stress to list. - vonMises.append(sqrt(pow(es[0], 2) - es[0] * - es[1] + pow(es[1], 2) + 3*es[2])) + vonMises.append(sqrt(pow(es[0], 2) - es[0] * es[1] + pow(es[1], 2) + 3 * es[2])) # es: [sigx sigy tauxy] @@ -146,21 +196,46 @@ cfu.info("Visualising...") cfv.figure() -cfv.draw_geometry(g, draw_points=True, label_curves=True, label_points=True, - title="Example 6a - Geometry") +cfv.draw_geometry( + g, + draw_points=True, + label_curves=True, + label_points=True, + title="Example 6a - Geometry", +) cfv.figure() -cfv.draw_mesh(coords, edof, dofs_per_node=mesh.dofs_per_node, - el_type=mesh.el_type, title="Example 6b - Meshing") +cfv.draw_mesh( + coords, + edof, + dofs_per_node=mesh.dofs_per_node, + el_type=mesh.el_type, + title="Example 6b - Meshing", +) cfv.figure() -cfv.draw_element_values(vonMises, coords, edof, mesh.dofs_per_node, mesh.el_type, None, - draw_elements=False, draw_undisplaced_mesh=False, title="Example 6c - Effective stress") +cfv.draw_element_values( + vonMises, + coords, + edof, + mesh.dofs_per_node, + mesh.el_type, + None, + draw_elements=False, + draw_undisplaced_mesh=False, + title="Example 6c - Effective stress", +) cfv.figure() -cfv.draw_displacements(a, coords, edof, mesh.dofs_per_node, mesh.el_type, - draw_undisplaced_mesh=True, - title="Example 6d - Displacements") +cfv.draw_displacements( + a, + coords, + edof, + mesh.dofs_per_node, + mesh.el_type, + draw_undisplaced_mesh=True, + title="Example 6d - Displacements", +) # Make use of attribute 'nodesOnCurve' in GmshMesher to draw some arrows on # the right hand side of the mesh: --> currently not working @@ -170,18 +245,16 @@ # 4 and 5 are the IDs of the curves where we applied the forces. for curveID in [4, 5]: - # Get the nodes, without duplicates. rightSideNodes = rightSideNodes.union(set(mesh.nodesOnCurve[curveID])) for i in rightSideNodes: - # Position of the node with displacements. - x = coords[i, 0] + a[i*2, 0] - y = coords[i, 1] + a[i*2+1, 0] + x = coords[i, 0] + a[i * 2, 0] + y = coords[i, 1] + a[i * 2 + 1, 0] # A poor man's force indicator. Could also use vv.plot() - cfv.add_text(r'$\rightarrow$', (x, y), color='g') + cfv.add_text(r"$\rightarrow$", (x, y), color="g") # Enter main loop cfv.show_and_wait() diff --git a/examples/exm_stress_2d_export.py b/examples/exm_stress_2d_export.py index 3aa9af2..4cf2e3e 100644 --- a/examples/exm_stress_2d_export.py +++ b/examples/exm_stress_2d_export.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -'''Example 06 +"""Example 06 Solves a plane stress 2D problem using a structured mesh. Shows how to draw von Mises effective stress as an element value with drawElementValues(). Shows use of GmshMesher attribute 'nodesOnCurve' (dictionary that says which nodes are on a given geometry curve) -''' +""" import calfem.geometry as cfg import calfem.mesh as cfm @@ -25,8 +25,8 @@ v = 0.35 E = 2.1e9 ptype = 1 -ep = [ptype,t] -D=cfc.hooke(ptype, E, v) +ep = [ptype, t] +D = cfc.hooke(ptype, E, v) # ---- Define geometry ------------------------------------------------------ @@ -36,52 +36,104 @@ # Just a shorthand. We use this to make the circle arcs. -s2 = 1/sqrt(2) +s2 = 1 / sqrt(2) + +points = [ + [0, 3], + [2.5, 3], + [3, 3], + [4 - s2, 3 - s2], + [4, 2], # 0-4 + [4 + s2, 3 - s2], + [5, 3], + [5.5, 3], + [8, 3], + [0, 1.5], # 5-9 + [2.5, 1.5], + [4, 1.5], + [5.5, 1.5], + [8, 1.5], + [0, 0], # 10-14 + [2.5, 0], + [3, 0], + [4 - s2, s2], + [4, 1], + [4 + s2, s2], # 15-19 + [5, 0], + [5.5, 0], + [8, 0], + [4, 3], + [4, 0], +] # 20-24 -points = [[0, 3], [2.5, 3], [3, 3], [4-s2, 3-s2], [4, 2], #0-4 - [4+s2, 3-s2], [5, 3], [5.5, 3], [8,3], [0, 1.5], #5-9 - [2.5, 1.5], [4, 1.5], [5.5, 1.5], [8, 1.5], [0, 0], #10-14 - [2.5, 0], [3, 0], [4-s2, s2], [4, 1], [4+s2, s2], #15-19 - [5, 0], [5.5, 0], [8,0], [4,3], [4,0]] #20-24 - for xp, yp in points: - g.point([xp*0.1, yp*0.1]) - -splines = [[0,1], [1,2], [6,7], [7,8], [8,13], #0-4 - [13,22], [22,21], [21,20], [16,15], [15,14], #5-9 - [14,9], [9,0], [9,10], [10,1], [10, 15], #10-14 - [10,11], [11,4], [11,18], [11,12], [12,7], #15-19 - [12,21], [12,13], [3,10], [5,12], [10,17], #20-24 - [12,19]] #25 - + g.point([xp * 0.1, yp * 0.1]) + +splines = [ + [0, 1], + [1, 2], + [6, 7], + [7, 8], + [8, 13], # 0-4 + [13, 22], + [22, 21], + [21, 20], + [16, 15], + [15, 14], # 5-9 + [14, 9], + [9, 0], + [9, 10], + [10, 1], + [10, 15], # 10-14 + [10, 11], + [11, 4], + [11, 18], + [11, 12], + [12, 7], # 15-19 + [12, 21], + [12, 13], + [3, 10], + [5, 12], + [10, 17], # 20-24 + [12, 19], +] # 25 + for s in splines: g.spline(s, el_on_curve=10) - -g.curveMarker(ID=4, marker=7) #Assign marker 7 to the splines on the right. -g.curveMarker(ID=5, marker=7) # We will apply a force on nodes with marker 7. -g.curveMarker(ID=10, marker=5) #Assign marker 5 to the splines on the left. -g.curveMarker(ID=11, marker=5) # The nodes with marker 5 will be locked in place. + +g.curveMarker(ID=4, marker=7) # Assign marker 7 to the splines on the right. +g.curveMarker(ID=5, marker=7) # We will apply a force on nodes with marker 7. +g.curveMarker(ID=10, marker=5) # Assign marker 5 to the splines on the left. +g.curveMarker(ID=11, marker=5) # The nodes with marker 5 will be locked in place. # Points in circle arcs are [start, center, end] -circlearcs = [[2, 23, 3], [3, 23, 4], [4, 23, 5], [5, 23, 6], #26-29 - [16, 24, 17], [17, 24, 18], [18, 24, 19], [19, 24, 20]] #30-33 - +circlearcs = [ + [2, 23, 3], + [3, 23, 4], + [4, 23, 5], + [5, 23, 6], # 26-29 + [16, 24, 17], + [17, 24, 18], + [18, 24, 19], + [19, 24, 20], +] # 30-33 + for c in circlearcs: g.circle(c, el_on_curve=10) -g.structuredSurface([11,12,13,0]) #0 +g.structuredSurface([11, 12, 13, 0]) # 0 g.structuredSurface([14, 12, 10, 9]) g.structuredSurface([8, 30, 24, 14]) g.structuredSurface([24, 31, 17, 15]) -g.structuredSurface([15, 16, 27, 22]) #4 -g.structuredSurface([22, 26, 1, 13]) +g.structuredSurface([15, 16, 27, 22]) # 4 +g.structuredSurface([22, 26, 1, 13]) g.structuredSurface([16, 18, 23, 28]) g.structuredSurface([19, 2, 29, 23]) -g.structuredSurface([19, 21, 4, 3]) #8 +g.structuredSurface([19, 21, 4, 3]) # 8 g.structuredSurface([20, 6, 5, 21]) g.structuredSurface([25, 20, 7, 33]) -g.structuredSurface([32, 17, 18, 25]) #11 +g.structuredSurface([32, 17, 18, 25]) # 11 # ---- Create mesh ---------------------------------------------------------- @@ -89,7 +141,7 @@ # Create mesh -mesh = cfm.GmshMesh(geometry = g) +mesh = cfm.GmshMesh(geometry=g) mesh.el_type = 3 mesh.dofs_per_node = 2 @@ -102,7 +154,7 @@ nDofs = np.size(dofs) ex, ey = cfc.coordxtr(edof, coords, dofs) -K = np.zeros([nDofs,nDofs]) +K = np.zeros([nDofs, nDofs]) for eltopo, elx, ely in zip(edof, ex, ey): Ke = cfc.planqe(elx, ely, ep, D) @@ -110,44 +162,53 @@ cfu.info("Solving equation system...") -f = np.zeros([nDofs,1]) +f = np.zeros([nDofs, 1]) -bc = np.array([],'i') -bcVal = np.array([],'f') +bc = np.array([], "i") +bcVal = np.array([], "f") bc, bcVal = cfu.applybc(bdofs, bc, bcVal, 5, 0.0, 0) cfu.applyforcetotal(bdofs, f, 7, 10e5, 1) -a,r = cfc.solveq(K,f,bc,bcVal) +a, r = cfc.solveq(K, f, bc, bcVal) cfu.info("Computing element forces...") -ed = cfc.extractEldisp(edof,a) +ed = cfc.extractEldisp(edof, a) von_mises = [] principal1 = [] principal2 = [] # For each element: -for i in range(edof.shape[0]): - +for i in range(edof.shape[0]): # Determine element stresses and strains in the element. - - es, et = cfc.planqs(ex[i,:], ey[i,:], ep, D, ed[i,:]) - - # Calc and append effective stress to list. - - von_mises.append( sqrt( pow(es[0],2) - es[0]*es[1] + pow(es[1],2) + 3*es[2] ) ) - - theta = np.arctan2(2*es[2], es[0]-es[1])*0.5 - sigma1 = 0.5*(es[0]+es[1])+np.sqrt(np.power(0.5*(es[0]-es[1]),2)+np.power(es[2],2)) - sigma2 = 0.5*(es[0]+es[1])-np.sqrt(np.power(0.5*(es[0]-es[1]),2)+np.power(es[2],2)) - s1v = [sigma1*np.cos(theta), sigma1*np.sin(theta), 0.0] - s2v = [sigma2*np.cos(theta+0.5*np.pi), sigma2*np.sin(theta+0.5*np.pi), 0.0] + + es, et = cfc.planqs(ex[i, :], ey[i, :], ep, D, ed[i, :]) + + # Calc and append effective stress to list. + + von_mises.append(sqrt(pow(es[0], 2) - es[0] * es[1] + pow(es[1], 2) + 3 * es[2])) + + theta = np.arctan2(2 * es[2], es[0] - es[1]) * 0.5 + sigma1 = 0.5 * (es[0] + es[1]) + np.sqrt( + np.power(0.5 * (es[0] - es[1]), 2) + np.power(es[2], 2) + ) + sigma2 = 0.5 * (es[0] + es[1]) - np.sqrt( + np.power(0.5 * (es[0] - es[1]), 2) + np.power(es[2], 2) + ) + s1v = [sigma1 * np.cos(theta), sigma1 * np.sin(theta), 0.0] + s2v = [ + sigma2 * np.cos(theta + 0.5 * np.pi), + sigma2 * np.sin(theta + 0.5 * np.pi), + 0.0, + ] principal1.append(s1v) principal2.append(s2v) # ---- Export to VTK -------------------------------------------------------- -cfu.export_vtk_stress("exm6.vtk", coords, mesh.topo-1, a, von_mises, principal1, principal2) +cfu.export_vtk_stress( + "exm6.vtk", coords, mesh.topo - 1, a, von_mises, principal1, principal2 +) diff --git a/examples/exm_stress_2d_materials.py b/examples/exm_stress_2d_materials.py index e12f068..fd7e11a 100644 --- a/examples/exm_stress_2d_materials.py +++ b/examples/exm_stress_2d_materials.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -'''Example 10 +"""Example 10 The use case from the user manual. The example does not contain anything that is not covered in the previous examples. -''' +""" import calfem.core as cfc import calfem.geometry as cfg @@ -12,6 +12,7 @@ import calfem.vis_mpl as cfv import calfem.utils as cfu import numpy as np +import math from scipy.sparse import lil_matrix @@ -24,7 +25,7 @@ E1 = 2e9 E2 = 0.2e9 ptype = 1 -ep = [ptype,t] +ep = [ptype, t] D1 = cfc.hooke(ptype, E1, v) D2 = cfc.hooke(ptype, E2, v) @@ -43,53 +44,53 @@ # Parameters controlling mesh -el_size_factor = 0.05 # Element size factor -el_type = 3 # Triangle element -dofs_per_node = 2 # Dof per node +el_size_factor = 0.05 # Element size factor +el_type = 3 # Triangle element +dofs_per_node = 2 # Dof per node # ---- Create Geometry ------------------------------------------------------ # Create a Geometry object that holds the geometry. -g = cfg.Geometry() +g = cfg.Geometry() # Add points: -g.point([0, 0]) #0 -g.point([1, 0]) #1 -g.point([1, 1]) #2 -g.point([0, 1]) #3 -g.point([0.2, 0.2]) #4 -g.point([0.8, 0.2]) #5 -g.point([0.8, 0.8]) #6 -g.point([0.2, 0.8]) #7 +g.point([0, 0]) # 0 +g.point([1, 0]) # 1 +g.point([1, 1]) # 2 +g.point([0, 1]) # 3 +g.point([0.2, 0.2]) # 4 +g.point([0.8, 0.2]) # 5 +g.point([0.8, 0.8]) # 6 +g.point([0.2, 0.8]) # 7 # Add curves: -g.spline([0, 1], marker = mark_fixed) #0 -g.spline([2, 1]) #1 -g.spline([3, 2], marker = mark_load) #2 -g.spline([0, 3]) #3 -g.spline([4, 5]) #4 -g.spline([5, 6]) #5 -g.spline([6, 7]) #6 -g.spline([7, 4]) #7 +g.spline([0, 1], marker=mark_fixed) # 0 +g.spline([2, 1]) # 1 +g.spline([3, 2], marker=mark_load) # 2 +g.spline([0, 3]) # 3 +g.spline([4, 5]) # 4 +g.spline([5, 6]) # 5 +g.spline([6, 7]) # 6 +g.spline([7, 4]) # 7 # Add surfaces: -g.surface([0,1,2,3], holes = [[4,5,6,7]], marker = mark_E1) -g.surface([4,5,6,7], marker = mark_E2) +g.surface([0, 1, 2, 3], holes=[[4, 5, 6, 7]], marker=mark_E1) +g.surface([4, 5, 6, 7], marker=mark_E2) # ---- Create Mesh ---------------------------------------------------------- mesh = cfm.GmshMeshGenerator(g) mesh.el_size_factor = el_size_factor -mesh.el_type = el_type +mesh.el_type = el_type mesh.dofs_per_node = dofs_per_node # Mesh the geometry: # The first four return values are the same as those that trimesh2d() returns. -# value elementmarkers is a list of markers, and is used for finding the +# value elementmarkers is a list of markers, and is used for finding the # marker of a given element (index). coords, edof, dofs, bdofs, elementmarkers = mesh.create() @@ -97,34 +98,33 @@ # ---- Solve problem -------------------------------------------------------- nDofs = np.size(dofs) -K = lil_matrix((nDofs,nDofs)) +K = lil_matrix((nDofs, nDofs)) ex, ey = cfc.coordxtr(edof, coords, dofs) -cfu.info("Assembling K... ("+str(nDofs)+")") +cfu.info("Assembling K... (" + str(nDofs) + ")") for eltopo, elx, ely, elMarker in zip(edof, ex, ey, elementmarkers): - if el_type == 2: Ke = cfc.plante(elx, ely, elprop[elMarker][0], elprop[elMarker][1]) else: Ke = cfc.planqe(elx, ely, elprop[elMarker][0], elprop[elMarker][1]) - + cfc.assem(eltopo, K, Ke) - + cfu.info("Applying bc and loads...") -bc = np.array([],'i') -bcVal = np.array([],'i') +bc = np.array([], "i") +bcVal = np.array([], "i") bc, bcVal = cfu.applybc(bdofs, bc, bcVal, mark_fixed, 0.0) -f = np.zeros([nDofs,1]) +f = np.zeros([nDofs, 1]) -cfu.applyforcetotal(bdofs, f, mark_load, value = -10e5, dimension=2) +cfu.applyforcetotal(bdofs, f, mark_load, value=-10e5, dimension=2) cfu.info("Solving system...") -a,r = cfc.spsolveq(K, f, bc, bcVal) +a, r = cfc.spsolveq(K, f, bc, bcVal) cfu.info("Extracting ed...") @@ -136,48 +136,85 @@ cfu.info("Element forces... ") for i in range(edof.shape[0]): - # Handle triangle elements - - if el_type == 2: - es, et = cfc.plants(ex[i,:], ey[i,:], - elprop[elementmarkers[i]][0], - elprop[elementmarkers[i]][1], - ed[i,:]) - - von_mises.append( np.math.sqrt( pow(es[0,0],2) - es[0,0]*es[0,1] + pow(es[0,1],2) + 3*pow(es[0,2],2) ) ) + + if el_type == 2: + es, et = cfc.plants( + ex[i, :], + ey[i, :], + elprop[elementmarkers[i]][0], + elprop[elementmarkers[i]][1], + ed[i, :], + ) + + von_mises.append( + np.math.sqrt( + pow(es[0, 0], 2) + - es[0, 0] * es[0, 1] + + pow(es[0, 1], 2) + + 3 * pow(es[0, 2], 2) + ) + ) else: - # Handle quad elements - - es, et = cfc.planqs(ex[i,:], ey[i,:], - elprop[elementmarkers[i]][0], - elprop[elementmarkers[i]][1], - ed[i,:]) - - von_mises.append( np.math.sqrt( pow(es[0],2) - es[0]*es[1] + pow(es[1],2) + 3*pow(es[2],2) ) ) - + + es, et = cfc.planqs( + ex[i, :], + ey[i, :], + elprop[elementmarkers[i]][0], + elprop[elementmarkers[i]][1], + ed[i, :], + ) + + von_mises.append( + math.sqrt( + pow(es[0], 2) - es[0] * es[1] + pow(es[1], 2) + 3 * pow(es[2], 2) + ) + ) + # ---- Visualise results ---------------------------------------------------- cfu.info("Drawing results...") -cfv.figure() +cfv.figure() cfv.draw_geometry(g, title="Geometry") -cfv.figure() -cfv.draw_mesh(coords=coords, edof=edof, dofs_per_node=dofs_per_node, el_type=el_type, - filled=True, title="Mesh") #Draws the mesh. +cfv.figure() +cfv.draw_mesh( + coords=coords, + edof=edof, + dofs_per_node=dofs_per_node, + el_type=el_type, + filled=True, + title="Mesh", +) # Draws the mesh. cfv.figure() -cfv.draw_displacements(a, coords, edof, dofs_per_node, el_type, - draw_undisplaced_mesh=False, title="Displacements", - magnfac=25.0) +cfv.draw_displacements( + a, + coords, + edof, + dofs_per_node, + el_type, + draw_undisplaced_mesh=False, + title="Displacements", + magnfac=25.0, +) cfv.figure() -cfv.draw_element_values(von_mises, coords, edof, dofs_per_node, el_type, a, - draw_elements=True, draw_undisplaced_mesh=False, - title="Effective Stress", magnfac=25.0) +cfv.draw_element_values( + von_mises, + coords, + edof, + dofs_per_node, + el_type, + a, + draw_elements=True, + draw_undisplaced_mesh=False, + title="Effective Stress", + magnfac=25.0, +) cfv.colorbar() diff --git a/examples/exm_stress_2d_pyvtk.py b/examples/exm_stress_2d_pyvtk.py index 8b67bce..5693bb0 100644 --- a/examples/exm_stress_2d_pyvtk.py +++ b/examples/exm_stress_2d_pyvtk.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -'''Example 06 +"""Example 06 Solves a plane stress 2D problem using a structured mesh. Shows how to draw von Mises effective stress as an element value with drawElementValues(). Shows use of GmshMesher attribute 'nodesOnCurve' (dictionary that says which nodes are on a given geometry curve) -''' +""" import calfem.geometry as cfg import calfem.mesh as cfm @@ -26,8 +26,8 @@ v = 0.35 E = 2.1e9 ptype = 1 -ep = [ptype,t] -D=cfc.hooke(ptype, E, v) +ep = [ptype, t] +D = cfc.hooke(ptype, E, v) # ---- Define geometry ------------------------------------------------------ @@ -37,52 +37,104 @@ # Just a shorthand. We use this to make the circle arcs. -s2 = 1/sqrt(2) +s2 = 1 / sqrt(2) + +points = [ + [0, 3], + [2.5, 3], + [3, 3], + [4 - s2, 3 - s2], + [4, 2], # 0-4 + [4 + s2, 3 - s2], + [5, 3], + [5.5, 3], + [8, 3], + [0, 1.5], # 5-9 + [2.5, 1.5], + [4, 1.5], + [5.5, 1.5], + [8, 1.5], + [0, 0], # 10-14 + [2.5, 0], + [3, 0], + [4 - s2, s2], + [4, 1], + [4 + s2, s2], # 15-19 + [5, 0], + [5.5, 0], + [8, 0], + [4, 3], + [4, 0], +] # 20-24 -points = [[0, 3], [2.5, 3], [3, 3], [4-s2, 3-s2], [4, 2], #0-4 - [4+s2, 3-s2], [5, 3], [5.5, 3], [8,3], [0, 1.5], #5-9 - [2.5, 1.5], [4, 1.5], [5.5, 1.5], [8, 1.5], [0, 0], #10-14 - [2.5, 0], [3, 0], [4-s2, s2], [4, 1], [4+s2, s2], #15-19 - [5, 0], [5.5, 0], [8,0], [4,3], [4,0]] #20-24 - for xp, yp in points: - g.point([xp*0.1, yp*0.1]) - -splines = [[0,1], [1,2], [6,7], [7,8], [8,13], #0-4 - [13,22], [22,21], [21,20], [16,15], [15,14], #5-9 - [14,9], [9,0], [9,10], [10,1], [10, 15], #10-14 - [10,11], [11,4], [11,18], [11,12], [12,7], #15-19 - [12,21], [12,13], [3,10], [5,12], [10,17], #20-24 - [12,19]] #25 - + g.point([xp * 0.1, yp * 0.1]) + +splines = [ + [0, 1], + [1, 2], + [6, 7], + [7, 8], + [8, 13], # 0-4 + [13, 22], + [22, 21], + [21, 20], + [16, 15], + [15, 14], # 5-9 + [14, 9], + [9, 0], + [9, 10], + [10, 1], + [10, 15], # 10-14 + [10, 11], + [11, 4], + [11, 18], + [11, 12], + [12, 7], # 15-19 + [12, 21], + [12, 13], + [3, 10], + [5, 12], + [10, 17], # 20-24 + [12, 19], +] # 25 + for s in splines: g.spline(s, el_on_curve=10) - -g.curveMarker(ID=4, marker=7) #Assign marker 7 to the splines on the right. -g.curveMarker(ID=5, marker=7) # We will apply a force on nodes with marker 7. -g.curveMarker(ID=10, marker=5) #Assign marker 5 to the splines on the left. -g.curveMarker(ID=11, marker=5) # The nodes with marker 5 will be locked in place. + +g.curveMarker(ID=4, marker=7) # Assign marker 7 to the splines on the right. +g.curveMarker(ID=5, marker=7) # We will apply a force on nodes with marker 7. +g.curveMarker(ID=10, marker=5) # Assign marker 5 to the splines on the left. +g.curveMarker(ID=11, marker=5) # The nodes with marker 5 will be locked in place. # Points in circle arcs are [start, center, end] -circlearcs = [[2, 23, 3], [3, 23, 4], [4, 23, 5], [5, 23, 6], #26-29 - [16, 24, 17], [17, 24, 18], [18, 24, 19], [19, 24, 20]] #30-33 - +circlearcs = [ + [2, 23, 3], + [3, 23, 4], + [4, 23, 5], + [5, 23, 6], # 26-29 + [16, 24, 17], + [17, 24, 18], + [18, 24, 19], + [19, 24, 20], +] # 30-33 + for c in circlearcs: g.circle(c, el_on_curve=10) -g.structuredSurface([11,12,13,0]) #0 +g.structuredSurface([11, 12, 13, 0]) # 0 g.structuredSurface([14, 12, 10, 9]) g.structuredSurface([8, 30, 24, 14]) g.structuredSurface([24, 31, 17, 15]) -g.structuredSurface([15, 16, 27, 22]) #4 -g.structuredSurface([22, 26, 1, 13]) +g.structuredSurface([15, 16, 27, 22]) # 4 +g.structuredSurface([22, 26, 1, 13]) g.structuredSurface([16, 18, 23, 28]) g.structuredSurface([19, 2, 29, 23]) -g.structuredSurface([19, 21, 4, 3]) #8 +g.structuredSurface([19, 21, 4, 3]) # 8 g.structuredSurface([20, 6, 5, 21]) g.structuredSurface([25, 20, 7, 33]) -g.structuredSurface([32, 17, 18, 25]) #11 +g.structuredSurface([32, 17, 18, 25]) # 11 # ---- Create mesh ---------------------------------------------------------- @@ -90,7 +142,7 @@ # Create mesh -mesh = cfm.GmshMesh(geometry = g) +mesh = cfm.GmshMesh(geometry=g) mesh.el_type = 3 mesh.dofs_per_node = 2 @@ -102,7 +154,7 @@ nDofs = np.size(dofs) ex, ey = cfc.coordxtr(edof, coords, dofs) -K = np.zeros([nDofs,nDofs]) +K = np.zeros([nDofs, nDofs]) for eltopo, elx, ely in zip(edof, ex, ey): Ke = cfc.planqe(elx, ely, ep, D) @@ -110,48 +162,57 @@ cfu.info("Solving equation system...") -f = np.zeros([nDofs,1]) +f = np.zeros([nDofs, 1]) -bc = np.array([],'i') -bcVal = np.array([],'f') +bc = np.array([], "i") +bcVal = np.array([], "f") bc, bcVal = cfu.applybc(bdofs, bc, bcVal, 5, 0.0, 0) cfu.applyforcetotal(bdofs, f, 7, 10e5, 1) -a,r = cfc.solveq(K,f,bc,bcVal) +a, r = cfc.solveq(K, f, bc, bcVal) cfu.info("Computing element forces...") -ed = cfc.extractEldisp(edof,a) +ed = cfc.extractEldisp(edof, a) von_mises = [] principal1 = [] principal2 = [] # For each element: -for i in range(edof.shape[0]): - +for i in range(edof.shape[0]): # Determine element stresses and strains in the element. - - es, et = cfc.planqs(ex[i,:], ey[i,:], ep, D, ed[i,:]) - - # Calc and append effective stress to list. - - von_mises.append( sqrt( pow(es[0],2) - es[0]*es[1] + pow(es[1],2) + 3*es[2] ) ) - - theta = np.arctan2(2*es[2], es[0]-es[1])*0.5 - sigma1 = 0.5*(es[0]+es[1])+np.sqrt(np.power(0.5*(es[0]-es[1]),2)+np.power(es[2],2)) - sigma2 = 0.5*(es[0]+es[1])-np.sqrt(np.power(0.5*(es[0]-es[1]),2)+np.power(es[2],2)) - s1v = [sigma1*np.cos(theta), sigma1*np.sin(theta), 0.0] - s2v = [sigma2*np.cos(theta+0.5*np.pi), sigma2*np.sin(theta+0.5*np.pi), 0.0] + + es, et = cfc.planqs(ex[i, :], ey[i, :], ep, D, ed[i, :]) + + # Calc and append effective stress to list. + + von_mises.append(sqrt(pow(es[0], 2) - es[0] * es[1] + pow(es[1], 2) + 3 * es[2])) + + theta = np.arctan2(2 * es[2], es[0] - es[1]) * 0.5 + sigma1 = 0.5 * (es[0] + es[1]) + np.sqrt( + np.power(0.5 * (es[0] - es[1]), 2) + np.power(es[2], 2) + ) + sigma2 = 0.5 * (es[0] + es[1]) - np.sqrt( + np.power(0.5 * (es[0] - es[1]), 2) + np.power(es[2], 2) + ) + s1v = [sigma1 * np.cos(theta), sigma1 * np.sin(theta), 0.0] + s2v = [ + sigma2 * np.cos(theta + 0.5 * np.pi), + sigma2 * np.sin(theta + 0.5 * np.pi), + 0.0, + ] principal1.append(s1v) principal2.append(s2v) # ---- Export to VTK -------------------------------------------------------- -points = coords.tolist() -polygons = (mesh.topo-1).tolist() +points = np.zeros([coords.shape[0], 3], dtype=np.float64) +points[:,0:2] = coords +points = points.tolist() +polygons = (mesh.topo - 1).tolist() displ = [] @@ -161,8 +222,8 @@ vectors2 = None cell_data = None -for i in range(0,len(a),2): - displ.append([np.asscalar(a[i]), np.asscalar(a[i+1]), 0.0]) +for i in range(0, len(a), 2): + displ.append([a[i].item(), a[i + 1].item(), 0.0]) point_data = vtk.PointData(vtk.Vectors(displ, name="displacements")) scalars = vtk.Scalars(von_mises, name="scalar") @@ -171,7 +232,7 @@ cell_data = vtk.CellData(scalars, vectors1, vectors2) -structure = vtk.PolyData(points = points, polygons = polygons) +structure = vtk.PolyData(points=points, polygons=polygons) vtk_data = vtk.VtkData(structure, cell_data, point_data) vtk_data.tofile("exm6.vtk", "ascii") diff --git a/examples/exm_stress_2d_qt.py b/examples/exm_stress_2d_qt.py index 3cec9b0..d461b16 100644 --- a/examples/exm_stress_2d_qt.py +++ b/examples/exm_stress_2d_qt.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -'''Example 06 +"""Example 06 Solves a plane stress 2D problem using a structured mesh. Shows how to draw von Mises effective stress as an element value with drawElementValues(). Shows use of GmshMesher attribute 'nodesOnCurve' (dictionary that says which nodes are on a given geometry curve) -''' +""" import sys from math import sqrt @@ -21,10 +21,12 @@ import calfem.utils as cfu import calfem.core as cfc -#cfu.enableLogging() +# cfu.enableLogging() + class PlaneStress2DProblem(object): """Encapsulates our plane stress problem in a single class""" + def __init__(self, t=0.2, v=0.35, E=2.1e9, ptype=1): """Setup intial problem values""" self.t = t @@ -42,52 +44,106 @@ def updateGeometry(self): # Just a shorthand. We use this to make the circle arcs. - s2 = 1/sqrt(2) - - points = [[0, 3], [2.5, 3], [3, 3], [4-s2, 3-s2], [4, 2], #0-4 - [4+s2, 3-s2], [5, 3], [5.5, 3], [8, 3], [0, 1.5], #5-9 - [2.5, 1.5], [4, 1.5], [5.5, 1.5], [8, 1.5], [0, 0], #10-14 - [2.5, 0], [3, 0], [4-s2, s2], [4, 1], [4+s2, s2], #15-19 - [5, 0], [5.5, 0], [8, 0], [4, 3], [4, 0]] #20-24 + s2 = 1 / sqrt(2) + + points = [ + [0, 3], + [2.5, 3], + [3, 3], + [4 - s2, 3 - s2], + [4, 2], # 0-4 + [4 + s2, 3 - s2], + [5, 3], + [5.5, 3], + [8, 3], + [0, 1.5], # 5-9 + [2.5, 1.5], + [4, 1.5], + [5.5, 1.5], + [8, 1.5], + [0, 0], # 10-14 + [2.5, 0], + [3, 0], + [4 - s2, s2], + [4, 1], + [4 + s2, s2], # 15-19 + [5, 0], + [5.5, 0], + [8, 0], + [4, 3], + [4, 0], + ] # 20-24 for xp, yp in points: - g.point([xp*0.1, yp*0.1]) - - splines = [[0, 1], [1, 2], [6, 7], [7, 8], [8, 13], #0-4 - [13, 22], [22, 21], [21, 20], [16, 15], [15, 14], #5-9 - [14, 9], [9, 0], [9, 10], [10, 1], [10, 15], #10-14 - [10, 11], [11, 4], [11, 18], [11, 12], [12, 7], #15-19 - [12, 21], [12, 13], [3, 10], [5, 12], [10, 17], #20-24 - [12, 19]] #25 + g.point([xp * 0.1, yp * 0.1]) + + splines = [ + [0, 1], + [1, 2], + [6, 7], + [7, 8], + [8, 13], # 0-4 + [13, 22], + [22, 21], + [21, 20], + [16, 15], + [15, 14], # 5-9 + [14, 9], + [9, 0], + [9, 10], + [10, 1], + [10, 15], # 10-14 + [10, 11], + [11, 4], + [11, 18], + [11, 12], + [12, 7], # 15-19 + [12, 21], + [12, 13], + [3, 10], + [5, 12], + [10, 17], # 20-24 + [12, 19], + ] # 25 for s in splines: g.spline(s, el_on_curve=5) - g.curveMarker(ID=4, marker=7) #Assign marker 7 to the splines on the right. - g.curveMarker(ID=5, marker=7) # We will apply a force on nodes with marker 7. - g.curveMarker(ID=10, marker=5) #Assign marker 5 to the splines on the left. - g.curveMarker(ID=11, marker=5) # The nodes with marker 5 will be locked in place. + g.curveMarker(ID=4, marker=7) # Assign marker 7 to the splines on the right. + g.curveMarker(ID=5, marker=7) # We will apply a force on nodes with marker 7. + g.curveMarker(ID=10, marker=5) # Assign marker 5 to the splines on the left. + g.curveMarker( + ID=11, marker=5 + ) # The nodes with marker 5 will be locked in place. # Points in circle arcs are [start, center, end] - circlearcs = [[2, 23, 3], [3, 23, 4], [4, 23, 5], [5, 23, 6], #26-29 - [16, 24, 17], [17, 24, 18], [18, 24, 19], [19, 24, 20]] #30-33 + circlearcs = [ + [2, 23, 3], + [3, 23, 4], + [4, 23, 5], + [5, 23, 6], # 26-29 + [16, 24, 17], + [17, 24, 18], + [18, 24, 19], + [19, 24, 20], + ] # 30-33 for c in circlearcs: g.circle(c, el_on_curve=5) - g.structuredSurface([11, 12, 13, 0]) #0 + g.structuredSurface([11, 12, 13, 0]) # 0 g.structuredSurface([14, 12, 10, 9]) g.structuredSurface([8, 30, 24, 14]) g.structuredSurface([24, 31, 17, 15]) - g.structuredSurface([15, 16, 27, 22]) #4 + g.structuredSurface([15, 16, 27, 22]) # 4 g.structuredSurface([22, 26, 1, 13]) g.structuredSurface([16, 18, 23, 28]) g.structuredSurface([19, 2, 29, 23]) - g.structuredSurface([19, 21, 4, 3]) #8 + g.structuredSurface([19, 21, 4, 3]) # 8 g.structuredSurface([20, 6, 5, 21]) g.structuredSurface([25, 20, 7, 33]) - g.structuredSurface([32, 17, 18, 25]) #11 + g.structuredSurface([32, 17, 18, 25]) # 11 def updateMesh(self): """Update mesh""" @@ -105,7 +161,13 @@ def updateMesh(self): self.mesh.el_type = self.el_type self.mesh.dofs_per_node = self.dofs_per_node - self.coords, self.edof, self.dofs, self.bdofs, self.elementmarkers = self.mesh.create() + ( + self.coords, + self.edof, + self.dofs, + self.bdofs, + self.elementmarkers, + ) = self.mesh.create() def solve(self): """Solve problem""" @@ -130,8 +192,8 @@ def solve(self): f = np.zeros([nDofs, 1]) - bc = np.array([], 'i') - bcVal = np.array([], 'i') + bc = np.array([], "i") + bcVal = np.array([], "i") bc, bcVal = cfu.applybc(self.bdofs, bc, bcVal, 5, 0.0, 0) @@ -147,14 +209,15 @@ def solve(self): # For each element: for i in range(self.edof.shape[0]): - # Determine element stresses and strains in the element. es, et = cfc.planqs(ex[i, :], ey[i, :], self.ep, self.D, ed[i, :]) # Calc and append effective stress to list. - self.vonMises.append(sqrt(pow(es[0], 2) - es[0]*es[1] + pow(es[1], 2) + 3*es[2])) + self.vonMises.append( + sqrt(pow(es[0], 2) - es[0] * es[1] + pow(es[1], 2) + 3 * es[2]) + ) ## es: [sigx sigy tauxy] @@ -168,19 +231,31 @@ def drawElementValues(self, figElementValues): """Draws element values in specified figure""" cfv.figure(figElementValues.nr) cfv.clf() - cfv.draw_element_values(self.vonMises, self.coords, self.edof, - self.mesh.dofs_per_node, self.mesh.el_type, - self.a, draw_elements=True, - draw_undisplaced_mesh=False, - title="Example 06 effective stress") + cfv.draw_element_values( + self.vonMises, + self.coords, + self.edof, + self.mesh.dofs_per_node, + self.mesh.el_type, + self.a, + draw_elements=True, + draw_undisplaced_mesh=False, + title="Example 06 effective stress", + ) def drawDisplacements(self, figDisplacements): """Draws displacements in specified figure""" cfv.figure(figDisplacements.nr) cfv.clf() - cfv.draw_displacements(self.a, self.coords, self.edof, - self.mesh.dofs_per_node, self.mesh.el_type, - draw_undisplaced_mesh=True, title="Example 06") + cfv.draw_displacements( + self.a, + self.coords, + self.edof, + self.mesh.dofs_per_node, + self.mesh.el_type, + draw_undisplaced_mesh=True, + title="Example 06", + ) # Make use of attribute 'nodesOnCurve' in GmshMesher to draw some arrows on # the right hand side of the mesh: @@ -190,32 +265,31 @@ def drawDisplacements(self, figDisplacements): # 4 and 5 are the IDs of the curves where we applied the forces. for curveID in [4, 5]: - # Get the nodes, without duplicates. rightSideNodes = rightSideNodes.union(set(self.mesh.nodesOnCurve[curveID])) for i in rightSideNodes: - # Position of the node with displacements. - x = self.coords[i, 0] + self.a[i*2, 0] - y = self.coords[i, 1] + self.a[i*2+1, 0] + x = self.coords[i, 0] + self.a[i * 2, 0] + y = self.coords[i, 1] + self.a[i * 2 + 1, 0] # A poor man's force indicator. Could also use vv.plot() - cfv.addText("\rightarrow", (x, y), font_size=20, color='g') + cfv.addText("\rightarrow", (x, y), font_size=20, color="g") class MainWindow(QMainWindow): """Main window class of our UI""" + def __init__(self): """Constructor""" super(MainWindow, self).__init__() # Load user interface from UI-file - loadUi('exm_stress_2d_qt.ui', self) + loadUi("exm_stress_2d_qt.ui", self) # Query for figure class name @@ -253,7 +327,6 @@ def on_executeButton_clicked(self): if __name__ == "__main__": - app = QApplication(sys.argv) widget = MainWindow() widget.show() diff --git a/examples/exm_structured_mesh.py b/examples/exm_structured_mesh.py index 88f21f2..e045363 100644 --- a/examples/exm_structured_mesh.py +++ b/examples/exm_structured_mesh.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -'''Example 04. +"""Example 04. Structured 3D meshing. Adding texts and labels to figures. Altering axis properties. -''' +""" import calfem.geometry as cfg import calfem.mesh as cfm @@ -63,7 +63,7 @@ # ---- Create mesh ---------------------------------------------------------- # Element type 5 is hexahedron. (See user manual for more element types) -el_type = 5 # error here, element type not implemented +el_type = 5 # error here, element type not implemented # Degrees of freedom per node. dofs_per_node = 1 @@ -90,32 +90,33 @@ # Draw mesh cfv.figure() -cfv.draw_mesh(coords=coords, edof=edof, - dofs_per_node=dofs_per_node, el_type=el_type, filled=True) +cfv.draw_mesh( + coords=coords, edof=edof, dofs_per_node=dofs_per_node, el_type=el_type, filled=True +) # Add a text in world space -#cfv.add_text("This is a Text", pos=(1, 0.5, 0.5), angle=45) +# cfv.add_text("This is a Text", pos=(1, 0.5, 0.5), angle=45) # Add a label in the screen space -#our_label = cfv.add_label("This is a Label", pos=(20,30), angle=-45) +# our_label = cfv.add_label("This is a Label", pos=(20,30), angle=-45) # We can change the attributes of labels and texts, such as color and position. -#our_label.text = "Label, changed." +# our_label.text = "Label, changed." # Make it red. (1,0,0) would also have worked. -#our_label.textColor = 'r' +# our_label.textColor = 'r' # Matlab style axes (three axes in the background instead of a cube) -#cfv.gca().axis.showBox = 0 +# cfv.gca().axis.showBox = 0 # Change the limits of the axes. -#cfv.gca().SetLimits(rangeX=(0,2), rangeY=(-1,1.5), rangeZ=(-0.5,2), margin=0.02) +# cfv.gca().SetLimits(rangeX=(0,2), rangeY=(-1,1.5), rangeZ=(-0.5,2), margin=0.02) # Enter main loop diff --git a/examples/exm_structured_mesh_3d.py b/examples/exm_structured_mesh_3d.py index fbf94c0..2c99beb 100644 --- a/examples/exm_structured_mesh_3d.py +++ b/examples/exm_structured_mesh_3d.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -'''Example 04. +"""Example 04. Structured 3D meshing. Adding texts and labels to figures. Altering axis properties. -''' +""" import calfem.geometry as cfg import calfem.mesh as cfm @@ -19,27 +19,27 @@ g.point([0.5, -0.3, -0.3], 1) g.point([1, 0, 0], 2) g.point([1, 1, 0], 3) -g.point([0, 1, 0], 4, marker = 11) # Set some markers no reason. -g.point([0, 0, 1], 5, marker = 11) # (markers can be given to points as well - # as curves and surfaces) -g.point([1, 0, 1], 6, marker = 11) +g.point([0, 1, 0], 4, marker=11) # Set some markers no reason. +g.point([0, 0, 1], 5, marker=11) # (markers can be given to points as well +# as curves and surfaces) +g.point([1, 0, 1], 6, marker=11) g.point([1, 1, 1], 7) g.point([0, 1, 1], 8) # Add splines -g.spline([0, 1, 2], 0, marker = 33, el_on_curve = 5) -g.spline([2, 3], 1, marker = 23, el_on_curve = 5) -g.spline([3, 4], 2, marker = 23, el_on_curve = 5) -g.spline([4, 0], 3, el_on_curve = 5) -g.spline([0, 5], 4, el_on_curve = 5) -g.spline([2, 6], 5, el_on_curve = 5) -g.spline([3, 7], 6, el_on_curve = 5) -g.spline([4, 8], 7, el_on_curve = 5) -g.spline([5, 6], 8, el_on_curve = 5) -g.spline([6, 7], 9, el_on_curve = 5) -g.spline([7, 8], 10, el_on_curve = 5) -g.spline([8, 5], 11, el_on_curve = 5) +g.spline([0, 1, 2], 0, marker=33, el_on_curve=5) +g.spline([2, 3], 1, marker=23, el_on_curve=5) +g.spline([3, 4], 2, marker=23, el_on_curve=5) +g.spline([4, 0], 3, el_on_curve=5) +g.spline([0, 5], 4, el_on_curve=5) +g.spline([2, 6], 5, el_on_curve=5) +g.spline([3, 7], 6, el_on_curve=5) +g.spline([4, 8], 7, el_on_curve=5) +g.spline([5, 6], 8, el_on_curve=5) +g.spline([6, 7], 9, el_on_curve=5) +g.spline([7, 8], 10, el_on_curve=5) +g.spline([8, 5], 11, el_on_curve=5) # Add surfaces @@ -51,32 +51,32 @@ g.structuredSurface([3, 4, 11, 7], 5) # Add Volume: -# addStructuredVolume() takes three args. The first is a list of surface IDs -# (structured surfaces). The surfaces should make a hexahedron +# addStructuredVolume() takes three args. The first is a list of surface IDs +# (structured surfaces). The surfaces should make a hexahedron # (i.e. 6 surfaces). Other kinds of structured volumes than hexahedra will -# not work for hexahedral elements, which is the only type of 3D element that -# CALFEM handles. The two optional parameters are the volume ID and +# not work for hexahedral elements, which is the only type of 3D element that +# CALFEM handles. The two optional parameters are the volume ID and # volume marker. -g.structuredVolume([0,1,2,3,4,5], 0, marker=90) +g.structuredVolume([0, 1, 2, 3, 4, 5], 0, marker=90) # ---- Create mesh ---------------------------------------------------------- # Element type 5 is hexahedron. (See user manual for more element types) -el_type = 5 +el_type = 5 # Degrees of freedom per node. -dofs_per_node = 1 +dofs_per_node = 1 # Create mesh coords, edof, dofs, bdofs, _ = cfm.mesh(g, el_type, 1, dofs_per_node) -#coords, edof, dofs, bdofs, _ = cfm.mesh( +# coords, edof, dofs, bdofs, _ = cfm.mesh( # g, el_type, dofs_per_node, gmsh_exec_path="D:\\vsmn20-software\\gmsh\gmsh.exe") # ---- Visualise mesh ------------------------------------------------------- -cfv.draw_mesh(coords, edof, el_type) \ No newline at end of file +cfv.draw_mesh(coords, edof, el_type) diff --git a/examples/exm_structured_mesh_vtk.py b/examples/exm_structured_mesh_vtk.py index d5d89a8..84c9a1a 100644 --- a/examples/exm_structured_mesh_vtk.py +++ b/examples/exm_structured_mesh_vtk.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -'''Example 03 +"""Example 03 Shows structured meshing in 2D. -''' +""" import calfem.geometry as cfg import calfem.mesh as cfm @@ -15,35 +15,37 @@ # Add Points: -g.point([0,0]) +g.point([0, 0]) g.point([1.2, 0]) g.point([1, 1.3]) g.point([0, 1]) g.point([2, 0.5]) # Add Splines: -# The first four curves are structured curves, i.e the number of nodes along -# the curves is pre-determined. Parameter el_on_curve states how many elements -# are placed along the curve. Parameters el_distrib_type and el_distrib_val are +# The first four curves are structured curves, i.e the number of nodes along +# the curves is pre-determined. Parameter el_on_curve states how many elements +# are placed along the curve. Parameters el_distrib_type and el_distrib_val are # optional parameters that specify how elements are distributed. # "bump" means elements are bunched up at the ends or the middle of the curve. # In this case el_distrib_val is smaller than 1, so elements crowd at the edges. # "progression" means each element along the curve is larger/smaller than the previous one. -# A larger el_distrib_val makes the elements larger at the end of the curves. - -g.spline([0,1], el_on_curve=10, el_distrib_type="bump", el_distrib_val=0.2) -g.spline([1,2], el_on_curve=20, el_distrib_type="progression", el_distrib_val=1.1) -g.spline([2,3], el_on_curve=10, el_distrib_type="bump", el_distrib_val=0.2) -g.spline([0,3], el_on_curve=20, el_distrib_type="progression", el_distrib_val=1.1) #Change order of points to reverse progression distribution +# A larger el_distrib_val makes the elements larger at the end of the curves. + +g.spline([0, 1], el_on_curve=10, el_distrib_type="bump", el_distrib_val=0.2) +g.spline([1, 2], el_on_curve=20, el_distrib_type="progression", el_distrib_val=1.1) +g.spline([2, 3], el_on_curve=10, el_distrib_type="bump", el_distrib_val=0.2) +g.spline( + [0, 3], el_on_curve=20, el_distrib_type="progression", el_distrib_val=1.1 +) # Change order of points to reverse progression distribution g.spline([2, 4, 1]) # Add Surfaces: -# A structured surface must contain 4 curves that have the parameter 'el_on_curve' -# defined. The number of elements on two opposite curves must be the same +# A structured surface must contain 4 curves that have the parameter 'el_on_curve' +# defined. The number of elements on two opposite curves must be the same # (In this case, curves 0 & 2 and 1 & 3). -g.structuredSurface([0,1,2,3]) -g.surface([4,1]) +g.structuredSurface([0, 1, 2, 3]) +g.surface([4, 1]) # ---- Create mesh ---------------------------------------------------------- @@ -51,11 +53,11 @@ # Element type 3 is quad. (2 is triangle. See user manual for more element types) -mesh.el_type = 3 +mesh.el_type = 3 # Degrees of freedom per node. -mesh.dofs_per_node = 1 +mesh.dofs_per_node = 1 mesh.el_size_factor = 0.01 # mesh.gmsh_exec_path = "D:\\vsmn20-software\\gmsh\gmsh.exe" @@ -71,8 +73,10 @@ # Draw mesh cfv.figure() -cfv.draw_mesh(coords, edof, dofs_per_node=mesh.dofs_per_node, el_type=mesh.el_type, filled=True) +cfv.draw_mesh( + coords, edof, dofs_per_node=mesh.dofs_per_node, el_type=mesh.el_type, filled=True +) # Enter main loop -cfv.show_and_wait() \ No newline at end of file +cfv.show_and_wait() diff --git a/examples/exm_temp_2d_markers.py b/examples/exm_temp_2d_markers.py index a0138f9..a662274 100644 --- a/examples/exm_temp_2d_markers.py +++ b/examples/exm_temp_2d_markers.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -'''Example 07 +"""Example 07 Meshing 8-node-isoparametric elements (second order incomplete quads). Shows use of surfacemarkers/elementmarkers to apply different properties to elements in different regions. -''' +""" import calfem.geometry as cfg import calfem.mesh as cfm @@ -32,14 +32,8 @@ n = 2 ep = [t, n] -D1 = np.matrix([ - [kx1, 0.], - [0., ky1] -]) -D2 = np.matrix([ - [kx2, 0.], - [0., ky2] -]) +D1 = np.matrix([[kx1, 0.0], [0.0, ky1]]) +D2 = np.matrix([[kx2, 0.0], [0.0, ky2]]) # markers 10 & 11 will be used to specify different regions with different # conductivity. @@ -52,15 +46,7 @@ # Add Points: -points = [ - [0, 0], - [0, 100], - [0, 150], - [100, 0], - [150, 0], - [100, -100], - [150, -100] -] +points = [[0, 0], [0, 100], [0, 150], [100, 0], [150, 0], [100, -100], [150, -100]] for p in points: g.point(p) @@ -112,7 +98,6 @@ K = np.zeros([n_dofs, n_dofs]) for eltopo, elx, ely, elMarker in zip(edof, ex, ey, elementmarkers): - # Calc element stiffness matrix: Conductivity matrix D is taken # from Ddict and depends on which region (which marker) the element is in. @@ -123,8 +108,8 @@ f = np.zeros([n_dofs, 1]) -bc = np.array([], 'i') -bc_val = np.array([], 'i') +bc = np.array([], "i") +bc_val = np.array([], "i") bc, bc_val = cfu.applybc(bdofs, bc, bc_val, 2, 30.0) bc, bc_val = cfu.applybc(bdofs, bc, bc_val, 3, 0.0) @@ -139,7 +124,8 @@ for i in range(np.shape(ex)[0]): es, et, eci = cfc.flw2i8s( - ex[i, :], ey[i, :], ep, Ddict[elementmarkers[i]], ed[i, :]) + ex[i, :], ey[i, :], ep, Ddict[elementmarkers[i]], ed[i, :] + ) # Do something with es, et, eci here. @@ -147,18 +133,24 @@ print("Visualising...") -cfv.set_figure_dpi(100) -cfv.figure(fig_size=(10, 10)) +cfv.figure() cfv.draw_geometry(g, title="Geometry") # 8-node quads are drawn as simple quads. -cfv.figure(fig_size=(10, 10)) +cfv.figure() cfv.draw_mesh(coords, edof, dofs_per_node, el_type, filled=False) -cfv.figure(fig_size=(10, 10)) -cfv.draw_nodal_values_shaded(a, coords, edof, title="Temperature", - dofs_per_node=mesh.dofs_per_node, el_type=mesh.el_type, draw_elements=True) +cfv.figure() +cfv.draw_nodal_values_shaded( + a, + coords, + edof, + title="Temperature", + dofs_per_node=mesh.dofs_per_node, + el_type=mesh.el_type, + draw_elements=True, +) cbar = cfv.colorbar(orientation="vertical") cbar.set_label("Temperature") diff --git a/examples/exm_temp_2d_splines_arcs.py b/examples/exm_temp_2d_splines_arcs.py index 97f6c9e..638c73a 100644 --- a/examples/exm_temp_2d_splines_arcs.py +++ b/examples/exm_temp_2d_splines_arcs.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -'''Example 01 +"""Example 01 Shows how to create simple geometry from splines and ellipse arcs, and how to mesh a quad mesh in GmshMesher. Also demonstrates drawGeometry(), drawMesh, and drawing texts and labels in a figure. -''' +""" import numpy as np @@ -27,10 +27,7 @@ n = 2 ep = [t, n] -D = np.matrix([ - [kx1, 0.], - [0., ky1] -]) +D = np.matrix([[kx1, 0.0], [0.0, ky1]]) # ---- Define geometry ------------------------------------------------------ @@ -85,7 +82,6 @@ K = np.zeros([n_dofs, n_dofs]) for el_topo, elx, ely, marker in zip(edof, ex, ey, element_markers): - # Calc element stiffness matrix: Conductivity matrix D is taken # from Ddict and depends on which region (which marker) the element is in. @@ -104,8 +100,8 @@ f = np.zeros([n_dofs, 1]) -bc = np.array([], 'i') -bc_val = np.array([], 'f') +bc = np.array([], "i") +bc_val = np.array([], "f") bc, bc_val = cfu.applybc(bdofs, bc, bc_val, id_outer, 30.0) bc, bc_val = cfu.applybc(bdofs, bc, bc_val, id_hole1, 300.0) @@ -139,21 +135,19 @@ # Draw the geometry. Note that surfaces and volumes are not drawn at all by # this function. -cfv.draw_geometry(g) - -# New figure window - cfv.figure() +cfv.draw_geometry(g) # Draw the mesh. +cfv.figure() cfv.draw_mesh( coords=coords, edof=edof, dofs_per_node=mesh.dofs_per_node, el_type=mesh.el_type, filled=True, - title="Example 01" + title="Example 01", ) cfv.figure() @@ -161,7 +155,15 @@ cfv.colorbar() cfv.figure() -cfv.draw_nodal_values_contourf(a, coords, edof, title="Temperature", dofs_per_node=mesh.dofs_per_node, el_type=mesh.el_type, draw_elements=True) +cfv.draw_nodal_values_contourf( + a, + coords, + edof, + title="Temperature", + dofs_per_node=mesh.dofs_per_node, + el_type=mesh.el_type, + draw_elements=True, +) cfv.colorbar() cfv.figure() @@ -173,7 +175,7 @@ # ourLabel = cfv.label("This is a Label", pos=(100,200), angle=-45) #Adds a label in the screen space # ourLabel.text = "Label, changed." #We can change the attributes of labels and texts, such as color, text, and position. # ourLabel.textColor = 'r' #Make it red. (1,0,0) would also have worked. -#ourLabel.position = (20,30) +# ourLabel.position = (20,30) # Enter main loop: diff --git a/examples/exm_tet_mesh.py b/examples/exm_tet_mesh.py index 464fc8a..a45888a 100644 --- a/examples/exm_tet_mesh.py +++ b/examples/exm_tet_mesh.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -'''Example 05 +"""Example 05 This example shows how to make an unstructured 3D mesh (tetrahedron elements, which calfem cant actually use). It also demonstrates how to do subplots and create two axes that are viewed from the same camera. -''' +""" import calfem.geometry as cfg import calfem.mesh as cfm @@ -14,27 +14,27 @@ g = cfg.geometry() -g.point([0, 0, 0], 0) -g.point([1, 0, 0], 1) -g.point([0, 1, 0], 2) -g.point([0, 1, 1], 3, el_size=0.1) +g.point([0, 0, 0], 0) +g.point([1, 0, 0], 1) +g.point([0, 1, 0], 2) +g.point([0, 1, 1], 3, el_size=0.1) g.point([0.5, -0.3, 0], 4) g.point([-0.3, 0.5, 0], 5) -g.point([0.75, 0.75, 0],6) +g.point([0.75, 0.75, 0], 6) -g.spline([0,4,1]) -g.spline([1,6,2]) -g.spline([2,5,0]) -g.spline([0,3]) -g.spline([3,2]) -g.spline([3,1]) +g.spline([0, 4, 1]) +g.spline([1, 6, 2]) +g.spline([2, 5, 0]) +g.spline([0, 3]) +g.spline([3, 2]) +g.spline([3, 1]) -g.ruledSurface([0,1,2]) -g.ruledSurface([0,5,3]) -g.ruledSurface([1,5,4]) -g.ruledSurface([2,3,4]) +g.ruledSurface([0, 1, 2]) +g.ruledSurface([0, 5, 3]) +g.ruledSurface([1, 5, 4]) +g.ruledSurface([2, 3, 4]) -g.volume([0,1,2,3]) +g.volume([0, 1, 2, 3]) # ---- Create mesh ---------------------------------------------------------- @@ -44,13 +44,13 @@ # Degrees of freedom per node. -dofs_per_node = 1 +dofs_per_node = 1 # Create mesh coords, edof, dofs, bdofs, elementmarkers = cfm.mesh(g, el_type, 0.3, dofs_per_node) -#coords, edof, dofs, bdofs, _ = cfm.mesh( +# coords, edof, dofs, bdofs, _ = cfm.mesh( # g, el_type, 0.3, dofs_per_node, gmsh_exec_path="D:\\vsmn20-software\\gmsh\gmsh.exe") @@ -67,8 +67,15 @@ # Draw geometry and mesh cfv.draw_geometry(g, axes=a1) -cfv.draw_mesh(coords=coords, edof=edof, dofs_per_node=dofs_per_node, el_type=el_type, filled=False, axes=a2) +cfv.draw_mesh( + coords=coords, + edof=edof, + dofs_per_node=dofs_per_node, + el_type=el_type, + filled=False, + axes=a2, +) # Enter main loop -cfv.show_and_wait() \ No newline at end of file +cfv.show_and_wait() diff --git a/examples/exm_tet_mesh_vtk.py b/examples/exm_tet_mesh_vtk.py index 2378b69..f158f55 100644 --- a/examples/exm_tet_mesh_vtk.py +++ b/examples/exm_tet_mesh_vtk.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -'''Example 05 +"""Example 05 This example shows how to make an unstructured 3D mesh (tetrahedron elements, which calfem cant actually use). It also demonstrates how to do subplots and create two axes that are viewed from the same camera. -''' +""" import calfem.geometry as cfg import calfem.mesh as cfm @@ -16,27 +16,27 @@ g = cfg.geometry() -g.point([0, 0, 0], 0) -g.point([1, 0, 0], 1) -g.point([0, 1, 0], 2) -g.point([0, 1, 1], 3, el_size=0.1) +g.point([0, 0, 0], 0) +g.point([1, 0, 0], 1) +g.point([0, 1, 0], 2) +g.point([0, 1, 1], 3, el_size=0.1) g.point([0.5, -0.3, 0], 4) g.point([-0.3, 0.5, 0], 5) -g.point([0.75, 0.75, 0],6) +g.point([0.75, 0.75, 0], 6) -g.spline([0,4,1]) -g.spline([1,6,2]) -g.spline([2,5,0]) -g.spline([0,3]) -g.spline([3,2]) -g.spline([3,1]) +g.spline([0, 4, 1]) +g.spline([1, 6, 2]) +g.spline([2, 5, 0]) +g.spline([0, 3]) +g.spline([3, 2]) +g.spline([3, 1]) -g.ruled_surface([0,1,2]) -g.ruled_surface([0,5,3]) -g.ruled_surface([1,5,4]) -g.ruled_surface([2,3,4]) +g.ruled_surface([0, 1, 2]) +g.ruled_surface([0, 5, 3]) +g.ruled_surface([1, 5, 4]) +g.ruled_surface([2, 3, 4]) -g.volume([0,1,2,3]) +g.volume([0, 1, 2, 3]) # ---- Create mesh ---------------------------------------------------------- @@ -46,16 +46,16 @@ # Degrees of freedom per node. -dofs_per_node = 1 +dofs_per_node = 1 # Create mesh coords, edof, dofs, bdofs, elementmarkers = cfm.mesh(g, el_type, 0.3, dofs_per_node) -#coords, edof, dofs, bdofs, _ = cfm.mesh( +# coords, edof, dofs, bdofs, _ = cfm.mesh( # g, el_type, 0.3, dofs_per_node, gmsh_exec_path="D:\\vsmn20-software\\gmsh\gmsh.exe") # ---- Visualise mesh ------------------------------------------------------- -cfv.draw_mesh(coords, edof, el_type) \ No newline at end of file +cfv.draw_mesh(coords, edof, el_type) diff --git a/examples/exm_tutorial_1.py b/examples/exm_tutorial_1.py new file mode 100644 index 0000000..bc0b095 --- /dev/null +++ b/examples/exm_tutorial_1.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +""" +Created on Sat Mar 3 22:08:29 2018 + +@author: Jonas Lindemann +""" + +import calfem.geometry as cfg +import calfem.mesh as cfm +import calfem.vis_mpl as cfv + +# ----- Define geometry + +g = cfg.Geometry() + +g.point([0.0, 0.0]) # point 0 +g.point([5.0, 0.0], marker=20) # point 1 +g.point([2.5, 4.0]) # point 2 + +g.spline([0, 1]) # line 0 +g.spline([1, 2]) # line 1 +g.spline([2, 0], marker=10) # line 2 + +g.surface([0, 1, 2]) + +# ----- Create mesh + +mesh = cfm.GmshMesh(g) + +mesh.elType = 2 # Degrees of freedom per node. +mesh.dofsPerNode = 1 # Factor that changes element sizes. +mesh.elSizeFactor = 0.15 + +coords, edof, dofs, bdofs, elementmarkers = mesh.create() + +print(bdofs) + +cfv.figure() +cfv.draw_geometry(g) + +# ----- Draw the mesh. + +cfv.figure() +cfv.draw_mesh( + coords=coords, + edof=edof, + dofs_per_node=mesh.dofsPerNode, + el_type=mesh.elType, + filled=True, + title="Example 01", +) + +cfv.showAndWait() diff --git a/examples/exm_tutorial_2.py b/examples/exm_tutorial_2.py new file mode 100644 index 0000000..f820e4e --- /dev/null +++ b/examples/exm_tutorial_2.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- +""" +Created on Sat Mar 3 22:08:29 2018 + +@author: Jonas Lindemann +""" + +import calfem.core as cfc +import calfem.geometry as cfg +import calfem.mesh as cfm +import calfem.vis_mpl as cfv +import calfem.utils as cfu + +import numpy as np +from math import * + +# ----- Problem parameters + +l = 5.0 +h = 1.0 +t = 0.2 + +v = 0.35 +E = 2.1e9 +ptype = 1 +ep = [ptype, t] +D = cfc.hooke(ptype, E, v) + +left_support = 10 +right_support = 20 +top_line = 30 + +# ----- Define geometry + +g = cfg.Geometry() + +g.point([0.0, 0.0], marker=left_support) # point 0 +g.point([l, 0.0], marker=right_support) # point 1 +g.point([l, h]) # point 2 +g.point([0.0, h]) # point 2 + +g.spline([0, 1]) # line 0 +g.spline([1, 2]) # line 1 +g.spline([2, 3], marker=top_line) # line 2 +g.spline([3, 0]) # line 2 + +g.surface([0, 1, 2, 3]) + +# ----- Create mesh + +mesh = cfm.GmshMesh(g) + +mesh.elType = 3 # Degrees of freedom per node. +mesh.dofsPerNode = 2 # Factor that changes element sizes. +mesh.elSizeFactor = 0.10 + +coords, edof, dofs, bdofs, elementmarkers = mesh.create() + +# ----- Solve problem + +nDofs = np.size(dofs) +ex, ey = cfc.coordxtr(edof, coords, dofs) + +K = np.zeros([nDofs, nDofs]) + +for eltopo, elx, ely in zip(edof, ex, ey): + Ke = cfc.planqe(elx, ely, ep, D) + cfc.assem(eltopo, K, Ke) + +bc = np.array([], "i") +bcVal = np.array([], "f") + +bc, bcVal = cfu.applybc(bdofs, bc, bcVal, left_support, 0.0, 0) +bc, bcVal = cfu.applybc(bdofs, bc, bcVal, right_support, 0.0, 2) + +f = np.zeros([nDofs, 1]) + +cfu.applyforcetotal(bdofs, f, top_line, -10e5, 2) + +a, r = cfc.solveq(K, f, bc, bcVal) + +ed = cfc.extractEldisp(edof, a) +vonMises = [] + +for i in range(edof.shape[0]): + es, et = cfc.planqs(ex[i, :], ey[i, :], ep, D, ed[i, :]) + vonMises.append(sqrt(pow(es[0], 2) - es[0] * es[1] + pow(es[1], 2) + 3 * es[2])) + +# ----- Draw geometry + +cfv.figure() +cfv.draw_geometry(g) + +# ----- Draw the mesh. + +cfv.figure() +cfv.draw_mesh( + coords=coords, + edof=edof, + dofs_per_node=mesh.dofsPerNode, + el_type=mesh.elType, + filled=True, + title="Example 01", +) + +# ----- Draw results + +cfv.figure() +cfv.draw_element_values( + vonMises, + coords, + edof, + mesh.dofs_per_node, + mesh.el_type, + a, + draw_elements=True, + draw_undisplaced_mesh=False, + title="Example 06 effective stress", +) + +cfv.figure() +cfv.draw_displacements( + a, + coords, + edof, + mesh.dofs_per_node, + mesh.el_type, + draw_undisplaced_mesh=True, + title="Example 06", + magnfac=10.0, +) + +cfv.showAndWait() diff --git a/examples/exn_bar2g.py b/examples/exn_bar2g.py index 629250e..b319189 100644 --- a/examples/exn_bar2g.py +++ b/examples/exn_bar2g.py @@ -1,67 +1,69 @@ # example exn_bar2g -#-------------------------------------------------------------------------- +# -------------------------------------------------------------------------- # PURPOSE # Analysis of a plane truss using second order theory. -#-------------------------------------------------------------------------- +# -------------------------------------------------------------------------- import numpy as np import calfem.core as cfc +import calfem.utils as cfu # ----- Topology ----- -edof = np.array([[1, 2, 5, 6], - [3, 4, 5, 6]]) +edof = np.array([[1, 2, 5, 6], [3, 4, 5, 6]]) # ----- Element properties and global coordinates ------------------------- -E=10e9 -A1=4e-2 -A2=1e-2 -ep1=np.array([E, A1]) -ep2=np.array([E, A2]) +E = 10e9 +A1 = 4e-2 +A2 = 1e-2 +ep1 = np.array([E, A1]) +ep2 = np.array([E, A2]) -ex1=np.array([0., 1.6]) -ey1=np.array([0., 0.]) -ex2=np.array([0., 1.6]) -ey2=np.array([1.2, 0]) +ex1 = np.array([0.0, 1.6]) +ey1 = np.array([0.0, 0.0]) +ex2 = np.array([0.0, 1.6]) +ey2 = np.array([1.2, 0]) # ----- Initial values for the iteration ---------------------------------- -eps=1e-6 # Error norm -QX1=0.01 -QX2=0; # Initial axial forces -QX01=1 # Axial force of the initial former iteration -n=0 # Iteration counter +eps = 1e-6 # Error norm +QX1 = 0.01 +QX2 = 0 +# Initial axial forces +QX01 = 1 # Axial force of the initial former iteration +n = 0 # Iteration counter # ----- Iteration procedure ----------------------------------------------- -while abs((QX1-QX01)/QX01) > eps: +while abs((QX1 - QX01) / QX01) > eps: n += 1 - K = np.zeros((6,6)) - f = np.zeros((6,1)) + K = np.zeros((6, 6)) + f = np.zeros((6, 1)) f[4] = -10e6 f[5] = -0.2e6 - Ke1 = cfc.bar2ge(ex1,ey1,ep1,QX1) - Ke2 = cfc.bar2ge(ex2,ey2,ep2,QX2) - K = cfc.assem(edof[0,:],K,Ke1) - K=cfc.assem(edof[1,:],K,Ke2) - bc = np.array([1,2,3,4]) - a, r = cfc.solveq(K,f,bc) + Ke1 = cfc.bar2ge(ex1, ey1, ep1, QX1) + Ke2 = cfc.bar2ge(ex2, ey2, ep2, QX2) + K = cfc.assem(edof[0, :], K, Ke1) + K = cfc.assem(edof[1, :], K, Ke2) + bc = np.array([1, 2, 3, 4]) + a, r = cfc.solveq(K, f, bc) - Ed = cfc.extract_ed(edof,a) + Ed = cfc.extract_ed(edof, a) QX01 = QX1 - es1, QX1 = cfc.bar2gs(ex1,ey1,ep1,Ed[0,:]) - es2, QX2 = cfc.bar2gs(ex2,ey2,ep2,Ed[1,:]) + es1, QX1 = cfc.bar2gs(ex1, ey1, ep1, Ed[0, :]) + es2, QX2 = cfc.bar2gs(ex2, ey2, ep2, Ed[1, :]) - if n>20: + if n > 20: print("The solution does not converge") break # ----- Results ----------------------------------------------- -print("Displacements:") -print(a) -print("Normal forces:") -print(QX1) -print(QX2) \ No newline at end of file + +cfu.disp_h1("Displacements:") +cfu.disp_array(a) +cfu.disp_h1("Normal forces:") +cfu.disp(QX1) +cfu.disp(QX2) diff --git a/examples/exn_bar2m.py b/examples/exn_bar2m.py index f9a5082..10510e4 100644 --- a/examples/exn_bar2m.py +++ b/examples/exn_bar2m.py @@ -1,101 +1,108 @@ # example exn_bar2m -#-------------------------------------------------------------------------- +# -------------------------------------------------------------------------- # PURPOSE # Analysis of a plane truss considering material nonlinearity. -#-------------------------------------------------------------------------- +# -------------------------------------------------------------------------- import numpy as np import calfem.core as cfc +import calfem.utils as cfu import calfem.vis_mpl as cfv edof = np.array([ - [1, 2, 5, 6], - [5, 6, 7, 8], + [1, 2, 5, 6], + [5, 6, 7, 8], [3, 4, 5, 6] ]) bc = np.array([1, 2, 3, 4, 7, 8]) ex = np.array([ - [0., 1.6], - [1.6, 1.6], - [0., 1.6] + [0.0, 1.6], + [1.6, 1.6], + [0.0, 1.6] ]) -ey = np.array([ - [0., 0.], - [0., 1.2], - [1.2, 0.] -]) +ey = np.array([[0.0, 0.0], [0.0, 1.2], [1.2, 0.0]]) E = np.array([200e9, 200e9, 200e9]) A = np.array([6.0e-4, 3.0e-4, 10.0e-4]) SY = 400e6 -Ns = (SY*A).reshape(-1,1) +Ns = (SY * A).reshape(-1, 1) dp = 4e3 incr = 100 -a = np.zeros((8,1)) -r = np.zeros((8,1)) -es = np.zeros((3,1)) +a = np.zeros((8, 1)) +r = np.zeros((8, 1)) +es = np.zeros((3, 1)) plbar = 0 -pl = np.array([0., 0.]) +pl = np.array([0.0, 0.0]) # Forward Euler increamental solution for i in range(incr): - K = np.zeros((8,8)) - df = np.zeros((8,1)) + K = np.zeros((8, 8)) + df = np.zeros((8, 1)) df[5] = -dp # Create and assemble element tangent stiffness matrix + for j in range(3): ep = np.array([E[j], A[j]]) - Ke = cfc.bar2e(ex[j,:],ey[j,:],ep) - K = cfc.assem(edof[j,:], K, Ke) + Ke = cfc.bar2e(ex[j, :], ey[j, :], ep) + K = cfc.assem(edof[j, :], K, Ke) # Stop iteration if determinant det(Kr) <= 0 - fdof = np.setdiff1d(np.arange(1,9),bc) - 1 - Kr = K[np.ix_(fdof,fdof)] + + fdof = np.setdiff1d(np.arange(1, 9), bc) - 1 + Kr = K[np.ix_(fdof, fdof)] if np.linalg.det(Kr) <= 0: print("Determinant zero after increment ", i) break # Solve for the displacement increment and determine total displacements - da, dr = cfc.solveq(K,df,bc) + + da, dr = cfc.solveq(K, df, bc) a += da r += dr # Determine normal forces in elements - ded = cfc.extract_ed(edof,da) - des = np.zeros((3,1)) + + ded = cfc.extract_ed(edof, da) + des = np.zeros((3, 1)) + for j in range(3): ep = np.array([E[j], A[j]]) - desj = cfc.bar2s(ex[j,:],ey[j,:],ep,ded[j,:]) - print('---') - print(desj) - print('---') - des[j,0] = desj[0] + desj = cfc.bar2s(ex[j, :], ey[j, :], ep, ded[j, :]) + des[j, 0] = desj[0] + es += des + for j in range(3): - if abs(es[j,0]) >= Ns[j]: + if abs(es[j, 0]) >= Ns[j]: E[j] = 0 # Determine if the stress in a bar has reached the yield stress - newplbar = np.sum(abs(es)>Ns) + + newplbar = np.sum(abs(es) > Ns) + if newplbar > plbar: plbar = newplbar - print(plbar, "plastic elements for increment ", i+1, " at load = ", (i+1)*dp) + print( + plbar, "plastic elements for increment ", i + 1, " at load = ", (i + 1) * dp + ) # Save variables for curve plotting - pl = np.vstack((pl, np.array([-a[5,0], (i+1)*dp]))) + + pl = np.vstack((pl, np.array([-a[5, 0], (i + 1) * dp]))) # Plot force-displacement relation -cfv.figure(1, fig_size=(7,4)) -cfv.plt.plot(pl[:,0],pl[:,1]) + +cfv.figure(1, fig_size=(7, 4)) +cfv.plt.plot(pl[:, 0], pl[:, 1]) cfv.plt.xlabel("Displacement") cfv.plt.ylabel("Force") -cfv.showAndWait() \ No newline at end of file +cfv.show_and_wait() diff --git a/examples/exn_beam2.py b/examples/exn_beam2.py index 6650870..bad147f 100644 --- a/examples/exn_beam2.py +++ b/examples/exn_beam2.py @@ -9,34 +9,34 @@ import calfem.vis_mpl as cfv -#----- Topology ------------------------------------------------- +# ----- Topology ------------------------------------------------- edof = np.array([ - [4, 5, 6, 1, 2, 3], - [7, 8, 9, 10, 11, 12], + [4, 5, 6, 1, 2, 3], + [7, 8, 9, 10, 11, 12], [4, 5, 6, 7, 8, 9] ]) -#----- Element stiffness and element load matrices ------------- +# ----- Element stiffness and element load matrices ------------- -E=200e9 -A1=2e-3 -A2=6e-3 -I1=1.6e-5 -I2=5.4e-5 +E = 200e9 +A1 = 2e-3 +A2 = 6e-3 +I1 = 1.6e-5 +I2 = 5.4e-5 ep1 = np.array([E, A1, I1]) ep3 = np.array([E, A2, I2]) -ex1 = np.array([0.,0.]) -ey1 = np.array([4.,0.]) -ex2 = np.array([6.,6.]) -ey2 = np.array([4.,0.]) -ex3 = np.array([0.,6.]) -ey3 = np.array([4.,4.]) +ex1 = np.array([0.0, 0.0]) +ey1 = np.array([4.0, 0.0]) +ex2 = np.array([6.0, 6.0]) +ey2 = np.array([4.0, 0.0]) +ex3 = np.array([0.0, 6.0]) +ey3 = np.array([4.0, 4.0]) -eq1 = np.array([0.]) -eq2 = np.array([0.]) +eq1 = np.array([0.0]) +eq2 = np.array([0.0]) eq3 = np.array([-50e3]) # ----- Initial axial forces ---------------------------------------------- @@ -49,100 +49,101 @@ # ----- Iteration for convergence ----------------------------------------- eps = 1e-6 -n=0 +n = 0 -while abs((QX1-QX01)/QX01) > eps: +while abs((QX1 - QX01) / QX01) > eps: n += 1 - K = np.zeros([12,12]) - f = np.zeros([12,1]) - f[3,0] = 10e3 + K = np.zeros([12, 12]) + f = np.zeros([12, 1]) + f[3, 0] = 10e3 - Ke1 = cfc.beam2ge(ex1,ey1,ep1,QX1) - Ke2 = cfc.beam2ge(ex2,ey2,ep1,QX2) - Ke3, fe3 = cfc.beam2ge(ex3,ey3,ep3,QX3,eq3) + Ke1 = cfc.beam2ge(ex1, ey1, ep1, QX1) + Ke2 = cfc.beam2ge(ex2, ey2, ep1, QX2) + Ke3, fe3 = cfc.beam2ge(ex3, ey3, ep3, QX3, eq3) - K = cfc.assem(edof[0,:],K,Ke1) - K = cfc.assem(edof[1,:],K,Ke2) - K, f = cfc.assem(edof[2,:],K,Ke3,f,fe3) + K = cfc.assem(edof[0, :], K, Ke1) + K = cfc.assem(edof[1, :], K, Ke2) + K, f = cfc.assem(edof[2, :], K, Ke3, f, fe3) bc = np.array([1, 2, 3, 10, 11]) a, r = cfc.solveq(K, f, bc) - ed = cfc.extract_ed(edof,a) + ed = cfc.extract_ed(edof, a) - QX01=QX1 - es1 = cfc.beam2gs(ex1,ey1,ep1,ed[0,:],QX1,eq1) - es2 = cfc.beam2gs(ex2,ey2,ep1,ed[1,:],QX2,eq2) - es3 = cfc.beam2gs(ex3,ey3,ep3,ed[2,:],QX3,eq3) + QX01 = QX1 + es1 = cfc.beam2gs(ex1, ey1, ep1, ed[0, :], QX1, eq1) + es2 = cfc.beam2gs(ex2, ey2, ep1, ed[1, :], QX2, eq2) + es3 = cfc.beam2gs(ex3, ey3, ep3, ed[2, :], QX3, eq3) QX1 = es1[1] QX2 = es2[1] QX3 = es3[1] - if n==1: + if n == 1: ed0 = ed - if n>20: + if n > 20: print("The solution does not converge") break -# #----- Section forces --------------------------------------- - -eq1 = np.array([0.,eq1.item()]) -eq2 = np.array([0.,eq2.item()]) -eq3 = np.array([0.,eq3.item()]) -es1, edi1, eci1 = cfc.beam2s(ex1,ey1,ep1,ed[0,:],eq1,21) -es2, edi2, eci2 = cfc.beam2s(ex2,ey2,ep1,ed[1,:],eq2,21) -es3, edi3, eci3 = cfc.beam2s(ex3,ey3,ep3,ed[2,:],eq3,21) - - -#----- Draw deformed frame --------------------------------------- -cfv.figure(1,fig_size=(6,4)) -plotpar=[3,1,0] -cfv.eldraw2(ex1,ey1,plotpar) -cfv.eldraw2(ex2,ey2,plotpar) -cfv.eldraw2(ex3,ey3,plotpar) -sfac=cfv.scalfact2(ex3,ey3,edi3,0.1) -plotpar=[1,2,1] -cfv.eldisp2(ex1,ey1,ed[0,:],plotpar,sfac) -cfv.eldisp2(ex2,ey2,ed[1,:],plotpar,sfac) -cfv.eldisp2(ex3,ey3,ed[2,:],plotpar,sfac) -plotpar=[2,4,2] -cfv.eldisp2(ex1,ey1,ed0[0,:],plotpar,sfac) -cfv.eldisp2(ex2,ey2,ed0[1,:],plotpar,sfac) -cfv.eldisp2(ex3,ey3,ed0[2,:],plotpar,sfac) +# ----- Section forces --------------------------------------- + +eq1 = np.array([0.0, eq1.item()]) +eq2 = np.array([0.0, eq2.item()]) +eq3 = np.array([0.0, eq3.item()]) +es1, edi1, eci1 = cfc.beam2s(ex1, ey1, ep1, ed[0, :], eq1, 21) +es2, edi2, eci2 = cfc.beam2s(ex2, ey2, ep1, ed[1, :], eq2, 21) +es3, edi3, eci3 = cfc.beam2s(ex3, ey3, ep3, ed[2, :], eq3, 21) + + +# ----- Draw deformed frame --------------------------------------- + +cfv.figure(1, fig_size=(6, 4)) +plotpar = [3, 1, 0] +cfv.eldraw2(ex1, ey1, plotpar) +cfv.eldraw2(ex2, ey2, plotpar) +cfv.eldraw2(ex3, ey3, plotpar) +sfac = cfv.scalfact2(ex3, ey3, edi3, 0.1) +plotpar = [1, 2, 1] +cfv.eldisp2(ex1, ey1, ed[0, :], plotpar, sfac) +cfv.eldisp2(ex2, ey2, ed[1, :], plotpar, sfac) +cfv.eldisp2(ex3, ey3, ed[2, :], plotpar, sfac) +plotpar = [2, 4, 2] +cfv.eldisp2(ex1, ey1, ed0[0, :], plotpar, sfac) +cfv.eldisp2(ex2, ey2, ed0[1, :], plotpar, sfac) +cfv.eldisp2(ex3, ey3, ed0[2, :], plotpar, sfac) cfv.plt.axis([-1.5, 7.5, -0.5, 5.5]) cfv.title("Displacements") -#----- Draw normal force diagram -------------------------------- -cfv.figure(2,fig_size=(6,4)) -plotpar=[2,1] -sfac=cfv.scalfact2(ex1,ey1,es1[:,0],0.2) -cfv.secforce2(ex1,ey1,es1[:,0],plotpar,sfac) -cfv.secforce2(ex2,ey2,es2[:,0],plotpar,sfac) -cfv.secforce2(ex3,ey3,es3[:,0],plotpar,sfac) +# ----- Draw normal force diagram -------------------------------- +cfv.figure(2, fig_size=(6, 4)) +plotpar = [2, 1] +sfac = cfv.scalfact2(ex1, ey1, es1[:, 0], 0.2) +cfv.secforce2(ex1, ey1, es1[:, 0], plotpar, sfac) +cfv.secforce2(ex2, ey2, es2[:, 0], plotpar, sfac) +cfv.secforce2(ex3, ey3, es3[:, 0], plotpar, sfac) cfv.plt.axis([-1.5, 7.5, -0.5, 5.5]) cfv.title("Normal force") -#----- Draw shear force diagram --------------------------------- -cfv.figure(3,fig_size=(6,4)) -plotpar=[2,1] -sfac=cfv.scalfact2(ex3,ey3,es3[:,1],0.2) -cfv.secforce2(ex1,ey1,es1[:,1],plotpar,sfac) -cfv.secforce2(ex2,ey2,es2[:,1],plotpar,sfac) -cfv.secforce2(ex3,ey3,es3[:,1],plotpar,sfac) +# ----- Draw shear force diagram --------------------------------- +cfv.figure(3, fig_size=(6, 4)) +plotpar = [2, 1] +sfac = cfv.scalfact2(ex3, ey3, es3[:, 1], 0.2) +cfv.secforce2(ex1, ey1, es1[:, 1], plotpar, sfac) +cfv.secforce2(ex2, ey2, es2[:, 1], plotpar, sfac) +cfv.secforce2(ex3, ey3, es3[:, 1], plotpar, sfac) cfv.plt.axis([-1.5, 7.5, -0.5, 5.5]) cfv.title("Shear force") -#----- Draw moment diagram -------------------------------------- -cfv.figure(4,fig_size=(6,4)) -plotpar=[2,1] -sfac=cfv.scalfact2(ex3,ey3,es3[:,2],0.2) -cfv.secforce2(ex1,ey1,es1[:,2],plotpar,sfac) -cfv.secforce2(ex2,ey2,es2[:,2],plotpar,sfac) -cfv.secforce2(ex3,ey3,es3[:,2],plotpar,sfac) +# ----- Draw moment diagram -------------------------------------- +cfv.figure(4, fig_size=(6, 4)) +plotpar = [2, 1] +sfac = cfv.scalfact2(ex3, ey3, es3[:, 2], 0.2) +cfv.secforce2(ex1, ey1, es1[:, 2], plotpar, sfac) +cfv.secforce2(ex2, ey2, es2[:, 2], plotpar, sfac) +cfv.secforce2(ex3, ey3, es3[:, 2], plotpar, sfac) cfv.plt.axis([-1.5, 7.5, -0.5, 5.5]) cfv.title("Bending moment") -cfv.showAndWait() -#------------------------ end ----------------------------------- \ No newline at end of file +cfv.show_and_wait() +# ------------------------ end ----------------------------------- diff --git a/examples/exn_beam2_b.py b/examples/exn_beam2_b.py index 0db602b..7ba99c8 100644 --- a/examples/exn_beam2_b.py +++ b/examples/exn_beam2_b.py @@ -9,34 +9,30 @@ import calfem.vis_mpl as cfv -#----- Topology ------------------------------------------------- +# ----- Topology ------------------------------------------------- -edof = np.array([ - [4, 5, 6, 1, 2, 3], - [7, 8, 9, 10, 11, 12], - [4, 5, 6, 7, 8, 9] -]) +edof = np.array([[4, 5, 6, 1, 2, 3], [7, 8, 9, 10, 11, 12], [4, 5, 6, 7, 8, 9]]) -#----- Element stiffness and element load matrices ------------- +# ----- Element stiffness and element load matrices ------------- -E=200e9 -A1=2e-3 -A2=6e-3 -I1=1.6e-5 -I2=5.4e-5 +E = 200e9 +A1 = 2e-3 +A2 = 6e-3 +I1 = 1.6e-5 +I2 = 5.4e-5 ep1 = np.array([E, A1, I1]) ep3 = np.array([E, A2, I2]) -ex1 = np.array([0.,0.]) -ey1 = np.array([4.,0.]) -ex2 = np.array([6.,6.]) -ey2 = np.array([4.,0.]) -ex3 = np.array([0.,6.]) -ey3 = np.array([4.,4.]) +ex1 = np.array([0.0, 0.0]) +ey1 = np.array([4.0, 0.0]) +ex2 = np.array([6.0, 6.0]) +ey2 = np.array([4.0, 0.0]) +ex3 = np.array([0.0, 6.0]) +ey3 = np.array([4.0, 4.0]) -eq1 = np.array([0.]) -eq2 = np.array([0.]) +eq1 = np.array([0.0]) +eq2 = np.array([0.0]) eq3 = np.array([-50e3]) # ----- Initial axial forces ---------------------------------------------- @@ -49,65 +45,65 @@ # ----- Iteration for convergence ----------------------------------------- eps = 1e-6 -n=0 +n = 0 -while abs((QX1-QX01)/QX01) > eps: +while abs((QX1 - QX01) / QX01) > eps: n += 1 - K = np.zeros([12,12]) - f = np.zeros([12,1]) - f[3,0] = 10e3 - - Ke1 = cfc.beam2ge(ex1,ey1,ep1,QX1) - Ke2 = cfc.beam2ge(ex2,ey2,ep1,QX2) - Ke3, fe3 = cfc.beam2ge(ex3,ey3,ep3,QX3,eq3) - - K = cfc.assem(edof[0,:],K,Ke1) - K = cfc.assem(edof[1,:],K,Ke2) - K, f = cfc.assem(edof[2,:],K,Ke3,f,fe3) - if n==1: + K = np.zeros([12, 12]) + f = np.zeros([12, 1]) + f[3, 0] = 10e3 + + Ke1 = cfc.beam2ge(ex1, ey1, ep1, QX1) + Ke2 = cfc.beam2ge(ex2, ey2, ep1, QX2) + Ke3, fe3 = cfc.beam2ge(ex3, ey3, ep3, QX3, eq3) + + K = cfc.assem(edof[0, :], K, Ke1) + K = cfc.assem(edof[1, :], K, Ke2) + K, f = cfc.assem(edof[2, :], K, Ke3, f, fe3) + if n == 1: K0 = K bc = np.array([1, 2, 3, 10, 11]) a, r = cfc.solveq(K, f, bc) - ed = cfc.extract_ed(edof,a) + ed = cfc.extract_ed(edof, a) - QX01=QX1 - es1 = cfc.beam2gs(ex1,ey1,ep1,ed[0,:],QX1,eq1) - es2 = cfc.beam2gs(ex2,ey2,ep1,ed[1,:],QX2,eq2) - es3 = cfc.beam2gs(ex3,ey3,ep3,ed[2,:],QX3,eq3) + QX01 = QX1 + es1 = cfc.beam2gs(ex1, ey1, ep1, ed[0, :], QX1, eq1) + es2 = cfc.beam2gs(ex2, ey2, ep1, ed[1, :], QX2, eq2) + es3 = cfc.beam2gs(ex3, ey3, ep3, ed[2, :], QX3, eq3) QX1 = es1[1] QX2 = es2[1] QX3 = es3[1] - if n>20: + if n > 20: print("The solution does not converge") break # ----- Buckling analysis ------------------------------------------------- -lam, phi = cfc.eigen(K,K0,bc) +lam, phi = cfc.eigen(K, K0, bc) one = np.ones(lam.shape) -alpha = np.divide(one,one-lam) +alpha = np.divide(one, one - lam) print(alpha[0]) # ----- Draw shape at instability ----------------------------------------- -Ed = cfc.extract_ed(edof,-phi[:,0]) -cfv.figure(1,fig_size=(6,4)) -plotpar=[3,1,0] -cfv.eldraw2(ex1,ey1,plotpar) -cfv.eldraw2(ex2,ey2,plotpar) -cfv.eldraw2(ex3,ey3,plotpar) -sfac=cfv.scalfact2(ex3,ey3,Ed[2,:],0.1) -plotpar=[1,2,1] -cfv.eldisp2(ex1,ey1,Ed[0,:],plotpar,sfac) -cfv.eldisp2(ex2,ey2,Ed[1,:],plotpar,sfac) -cfv.eldisp2(ex3,ey3,Ed[2,:],plotpar,sfac) +Ed = cfc.extract_ed(edof, -phi[:, 0]) +cfv.figure(1, fig_size=(6, 4)) +plotpar = [3, 1, 0] +cfv.eldraw2(ex1, ey1, plotpar) +cfv.eldraw2(ex2, ey2, plotpar) +cfv.eldraw2(ex3, ey3, plotpar) +sfac = cfv.scalfact2(ex3, ey3, Ed[2, :], 0.1) +plotpar = [1, 2, 1] +cfv.eldisp2(ex1, ey1, Ed[0, :], plotpar, sfac) +cfv.eldisp2(ex2, ey2, Ed[1, :], plotpar, sfac) +cfv.eldisp2(ex3, ey3, Ed[2, :], plotpar, sfac) cfv.plt.axis([-1.5, 7.5, -0.5, 5.5]) cfv.title("Shape at instability") -cfv.showAndWait() +cfv.show_and_wait() -#------------------------ end ----------------------------------- \ No newline at end of file +# ------------------------ end ----------------------------------- diff --git a/examples/experimental/exs6_test.py b/examples/experimental/exs6.py similarity index 100% rename from examples/experimental/exs6_test.py rename to examples/experimental/exs6.py diff --git a/examples/experimental/gmsh_api_test_2.py b/examples/experimental/gmsh_api.py similarity index 100% rename from examples/experimental/gmsh_api_test_2.py rename to examples/experimental/gmsh_api.py diff --git a/examples/experimental/gmsh_api_test.py b/examples/experimental/gmsh_api2.py similarity index 100% rename from examples/experimental/gmsh_api_test.py rename to examples/experimental/gmsh_api2.py diff --git a/examples/experimental/plot_test.py b/examples/experimental/plot_tst.py similarity index 100% rename from examples/experimental/plot_test.py rename to examples/experimental/plot_tst.py diff --git a/examples/experimental/test_point_in_geom.py b/examples/experimental/point_in_geom.py similarity index 100% rename from examples/experimental/test_point_in_geom.py rename to examples/experimental/point_in_geom.py diff --git a/examples/experimental/test_tri_mesh.py b/examples/experimental/tri_mesh.py similarity index 100% rename from examples/experimental/test_tri_mesh.py rename to examples/experimental/tri_mesh.py diff --git a/examples/exs1.py b/examples/exs1.py deleted file mode 100644 index ad79069..0000000 --- a/examples/exs1.py +++ /dev/null @@ -1,75 +0,0 @@ -# example exs1 -# ---------------------------------------------------------------- -# PURPOSE -# Linear elastic spring analysis. Introduction to the basic -# steps in the finite element method. -# ---------------------------------------------------------------- - -# REFERENCES -# P-E Austrell 1994-03-08 -# K-G Olsson 1995-09-28 -# O Dahlblom 2004-09-06 -# J Lindemann 2009-01-25 -# ---------------------------------------------------------------- - -import numpy as np -import calfem.core as cfc - -# ----- Topology matrix Edof ------------------------------------- - -Edof = np.array([ - [1,2], - [2,3], - [2,3] -]) - -# ----- Stiffness matrix K and load vector f --------------------- - -K = np.matrix(np.zeros((3,3))) -f = np.matrix(np.zeros((3,1))) - -# ----- Element stiffness matrices ------------------------------ - -k = 1500. -ep1 = k -ep2 = 2.*k -Ke1 = cfc.spring1e(ep1) -Ke2 = cfc.spring1e(ep2) - -# ----- Assemble Ke into K --------------------------------------- - -cfc.assem(Edof[0,:], K, Ke2) -cfc.assem(Edof[1,:], K, Ke1) -cfc.assem(Edof[2,:], K, Ke2) - -print("Stiffness matrix K:") -print(K) - -# ----- Solve the system of equations ---------------------------- - -bc = np.array([1,3]) -f[1]=100 - -a, r = cfc.solveq(K, f, bc) - -print("Displacements a:") -print(a) - -print("Reaction forces Q:") -print(r) - -# ----- Element forces ------------------------------------------- - -ed1 = cfc.extractEldisp(Edof[0,:],a) -ed2 = cfc.extractEldisp(Edof[1,:],a) -ed3 = cfc.extractEldisp(Edof[2,:],a) - -es1 = cfc.spring1s(ep2,ed1) -es2 = cfc.spring1s(ep1,ed2) -es3 = cfc.spring1s(ep2,ed3) - -print("N1 = "+str(es1)) -print("N2 = "+str(es2)) -print("N3 = "+str(es3)) - - diff --git a/examples/exs_bar2.py b/examples/exs_bar2.py index c1d1e20..0524c26 100644 --- a/examples/exs_bar2.py +++ b/examples/exs_bar2.py @@ -14,20 +14,23 @@ import numpy as np import calfem.core as cfc +import calfem.utils as cfu import calfem.vis_mpl as cfv +cfu.disp_h1("Analysis of a plane truss.") + # ----- Topology matrix Edof ------------------------------------- edof = np.array([ - [1, 2, 5, 6], - [5, 6, 7, 8], + [1, 2, 5, 6], + [5, 6, 7, 8], [3, 4, 5, 6] ]) # ----- Stiffness matrix K and load vector f --------------------- -K = np.matrix(np.zeros((8, 8))) -f = np.matrix(np.zeros((8, 1))) +K = np.array(np.zeros((8, 8))) +f = np.array(np.zeros((8, 1))) # ----- Element properties --------------------------------------- @@ -41,13 +44,13 @@ # ----- Element coordinates -------------------------------------- -ex1 = np.array([0., 1.6]) +ex1 = np.array([0.0, 1.6]) ex2 = np.array([1.6, 1.6]) -ex3 = np.array([0., 1.6]) +ex3 = np.array([0.0, 1.6]) -ey1 = np.array([0., 0.]) -ey2 = np.array([0., 1.2]) -ey3 = np.array([1.2, 0.]) +ey1 = np.array([0.0, 0.0]) +ey2 = np.array([0.0, 1.2]) +ey3 = np.array([1.2, 0.0]) # ----- Element stiffness matrices ------------------------------ @@ -57,12 +60,12 @@ # ----- Assemble Ke into K --------------------------------------- -cfc.assem(edof[0, :], K, Ke1) -cfc.assem(edof[1, :], K, Ke2) -cfc.assem(edof[2, :], K, Ke3) +K = cfc.assem(edof[0, :], K, Ke1) +K = cfc.assem(edof[1, :], K, Ke2) +K = cfc.assem(edof[2, :], K, Ke3) -print("Stiffness matrix K:") -print(K) +cfu.disp_h2("Stiffness matrix K:") +cfu.disp_array(K) # ----- Solve the system of equations ---------------------------- @@ -70,11 +73,11 @@ f[5] = -80e3 a, r = cfc.solveq(K, f, bc) -print("Displacements a:") -print(a) +cfu.disp_h2("Displacements a:") +cfu.disp_array(a) -print("Reaction forces r:") -print(r) +cfu.disp_h2("Reaction forces r:") +cfu.disp_array(r) # ----- Element forces ------------------------------------------- @@ -85,6 +88,8 @@ ed3 = cfc.extract_ed(edof[2, :], a) N3 = cfc.bar2s(ex3, ey3, ep3, ed3) +cfu.disp_h2("Element forces r:") + print("N1 = ") print(N1) print("N2 = ") @@ -110,19 +115,19 @@ cfv.eldisp2(ex3, ey3, ed3, plotpar, sfac) cfv.axis([-0.4, 2.0, -0.4, 1.4]) plotpar1 = 2 -cfv.scalgraph2(sfac,[1e-3, 0, -0.3],plotpar1) -cfv.title('Displacements') +cfv.scalgraph2(sfac, [1e-3, 0, -0.3], plotpar1) +cfv.title("Displacements") # ----- Draw normal force diagram -------------------------------- plotpar = [2, 1] -sfac = cfv.scalfact2(ex1, ey1, N2[:,0], 0.1) +sfac = cfv.scalfact2(ex1, ey1, N2[:, 0], 0.1) cfv.figure(2) -cfv.secforce2(ex1, ey1, N1[:,0], plotpar, sfac) -cfv.secforce2(ex2, ey2, N2[:,0], plotpar, sfac) -cfv.secforce2(ex3, ey3, N3[:,0], plotpar, sfac) +cfv.secforce2(ex1, ey1, N1[:, 0], plotpar, sfac) +cfv.secforce2(ex2, ey2, N2[:, 0], plotpar, sfac) +cfv.secforce2(ex3, ey3, N3[:, 0], plotpar, sfac) cfv.axis([-0.4, 2.0, -0.4, 1.4]) -cfv.scalgraph2(sfac,[5e4, 0, -0.3],plotpar1) -cfv.title('Normal force') +cfv.scalgraph2(sfac, [5e4, 0, -0.3], plotpar1) +cfv.title("Normal force") -cfv.showAndWait() +cfv.show_and_wait() diff --git a/examples/exs_bar2_la.py b/examples/exs_bar2_la.py index a774b0f..929f362 100644 --- a/examples/exs_bar2_la.py +++ b/examples/exs_bar2_la.py @@ -16,6 +16,9 @@ import numpy as np import calfem.core as cfc +import calfem.utils as cfu + +cfu.disp_h1("Analysis of a plane truss using loops") # ----- Topology matrix Edof ------------------------------------- @@ -29,15 +32,15 @@ [3, 4, 5, 6], [7, 8, 9, 10], [1, 2, 7, 8], - [5, 6, 11, 12] + [5, 6, 11, 12], ]) # ----- Stiffness matrix K and load vector f --------------------- K = np.zeros([12, 12]) f = np.zeros([12, 1]) -f[10] = 0.5e6*np.sin(np.pi/6) -f[11] = -0.5e6*np.cos(np.pi/6) +f[10] = 0.5e6 * np.sin(np.pi / 6) +f[11] = -0.5e6 * np.cos(np.pi / 6) # ----- Element properties --------------------------------------- @@ -48,29 +51,29 @@ # ----- Element coordinates -------------------------------------- ex = np.array([ - [0., 2.], - [0., 2.], - [2., 4.], - [2., 4.], - [2., 2.], - [4., 4.], - [0., 2.], - [2., 4.], - [0., 2.], - [2., 4.] + [0.0, 2.0], + [0.0, 2.0], + [2.0, 4.0], + [2.0, 4.0], + [2.0, 2.0], + [4.0, 4.0], + [0.0, 2.0], + [2.0, 4.0], + [0.0, 2.0], + [2.0, 4.0], ]) ey = np.array([ - [2., 2.], - [0., 0.], - [2., 2.], - [0., 0.], - [0., 2.], - [0., 2.], - [0., 2.], - [0., 2.], - [2., 0.], - [2., 0.] + [2.0, 2.0], + [0.0, 0.0], + [2.0, 2.0], + [0.0, 0.0], + [0.0, 2.0], + [0.0, 2.0], + [0.0, 2.0], + [0.0, 2.0], + [2.0, 0.0], + [2.0, 0.0], ]) # ----- Create element stiffness matrices Ke and assemble into K - @@ -79,31 +82,30 @@ Ke = cfc.bar2e(elx, ely, ep) cfc.assem(eltopo, K, Ke) -print("Stiffness matrix K:") -print(K) +cfu.disp_h2("Stiffness matrix K:") +cfu.disp_array(K) # ----- Solve the system of equations ---------------------------- bc = np.array([1, 2, 3, 4]) a, r = cfc.solveq(K, f, bc) -print("Displacements a:") -print(a) +cfu.disp_h2("Displacements a:") +cfu.disp_array(a) -print("Reaction forces r:") -print(r) +cfu.disp_h2("Reaction forces r:") +cfu.disp_array(r) # ----- Element forces ------------------------------------------- ed = cfc.extract_ed(edof, a) N = np.zeros([edof.shape[0]]) -print("Element forces:") +cfu.disp_h2("Element forces:") i = 0 for elx, ely, eld in zip(ex, ey, ed): es = cfc.bar2s(elx, ely, ep, eld) N[i] = es[0] - print("N%d = %g" % (i+1, N[i])) + print("N%d = %g" % (i + 1, N[i])) i += 1 - diff --git a/examples/exs_bar2_lb.py b/examples/exs_bar2_lb.py index 2fb3d3b..e96bd1d 100644 --- a/examples/exs_bar2_lb.py +++ b/examples/exs_bar2_lb.py @@ -3,7 +3,7 @@ # example exs_bar2_la # ---------------------------------------------------------------- # PURPOSE -# Analysis of a plane truss using loops and extraction of +# Analysis of a plane truss using loops and extraction of # element coordinates from a global coordinate matrix. # ---------------------------------------------------------------- @@ -18,6 +18,9 @@ import numpy as np import calfem.core as cfc +import calfem.utils as cfu + +cfu.disp_h1("Analysis of a plane truss using loops and extraction of element coordinates from a global coordinate matrix.") # ----- Topology matrix Edof ------------------------------------- @@ -31,15 +34,15 @@ [3, 4, 5, 6], [7, 8, 9, 10], [1, 2, 7, 8], - [5, 6, 11, 12] + [5, 6, 11, 12], ]) # ----- Stiffness matrix K and load vector f --------------------- K = np.zeros([12, 12]) f = np.zeros([12, 1]) -f[10] = 0.5e6*np.sin(np.pi/6) -f[11] = -0.5e6*np.cos(np.pi/6) +f[10] = 0.5e6 * np.sin(np.pi / 6) +f[11] = -0.5e6 * np.cos(np.pi / 6) # ----- Element properties --------------------------------------- @@ -47,61 +50,60 @@ E = 2.1e11 ep = [E, A] -#----- Global coordinates and topology -------------------------- +# ----- Global coordinates and topology -------------------------- coord = np.array([ - [0, 2], - [0, 0], - [2, 2], - [2, 0], - [4, 2], + [0, 2], + [0, 0], + [2, 2], + [2, 0], + [4, 2], [4, 0] ]) - + dof = np.array([ - [ 1, 2], - [ 3, 4], - [ 5, 6], - [ 7, 8], - [ 9, 10], + [1, 2], + [3, 4], + [5, 6], + [7, 8], + [9, 10], [11, 12] -]) +]) # ----- Element coordinates -------------------------------------- -ex, ey = cfc.coordxtr(edof, coord, dof,2) - +ex, ey = cfc.coordxtr(edof, coord, dof, 2) + # ----- Create element stiffness matrices Ke and assemble into K - for elx, ely, eltopo in zip(ex, ey, edof): Ke = cfc.bar2e(elx, ely, ep) cfc.assem(eltopo, K, Ke) -print("Stiffness matrix K:") -print(K) +cfu.disp_h2("Stiffness matrix K:") +cfu.disp_array(K) # ----- Solve the system of equations ---------------------------- bc = np.array([1, 2, 3, 4]) a, r = cfc.solveq(K, f, bc) -print("Displacements a:") -print(a) +cfu.disp_h2("Displacements a:") +cfu.disp_array(a) -print("Reaction forces r:") -print(r) +cfu.disp_h2("Reaction forces r:") +cfu.disp_array(r) # ----- Element forces ------------------------------------------- ed = cfc.extract_ed(edof, a) N = np.zeros([edof.shape[0]]) -print("Element forces:") +cfu.disp_h2("Element forces:") i = 0 for elx, ely, eld in zip(ex, ey, ed): es = cfc.bar2s(elx, ely, ep, eld) N[i] = es[0] - print("N%d = %g" % (i+1, N[i])) + print("N%d = %g" % (i + 1, N[i])) i += 1 - diff --git a/examples/exs_beam1.ipynb b/examples/exs_beam1.ipynb new file mode 100644 index 0000000..5a203de --- /dev/null +++ b/examples/exs_beam1.ipynb @@ -0,0 +1,636 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "21747083-42dc-453e-8d50-f582c0f8f220", + "metadata": {}, + "source": [ + "# Analysis of a plane frame" + ] + }, + { + "attachments": { + "59e815f8-32ca-458d-a624-3a504e74e306.png": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA5YAAAIQCAIAAAB192ZYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAASdEVYdFNvZnR3YXJlAEdyZWVuc2hvdF5VCAUAAGXYSURBVHhe7d0HVBTX38ZxUDHNJEYT/0lMIgpiRUAQC1bsvXclKvbeNRbsvWDvGnvvvfcusWHDBqKoWBAUBGSXvBf2xnfBBens7H4/554cfnd2F+OMs8/M3Llj8i8AAACgKERYAAAAKAwRFgAAAApDhAUAAIDCEGEBAACgMERYAAAAKAwRFgAAAApDhAUAAIDCEGEBAACgMERYAAAAKAwRFgAAAApDhAUAAIDCEGEBAACgMERYAAAAKAwRFgAAAApDhAUAAIDCEGEBAACgMERYAAAAKAwRFlCkyMhI+VMivbu43K1X506urh3axWyu7Tt17NJrgNvUBZtO3A1UyZcDAKCPiLCA8nTp1Mkil3lISIisE0sVdGtFhxK5zcWHWOQu3nzgxNkLl69dt3b14hmjOtcubik6rav1+PtKoFq+HgAAPUOEBZQnuRH233/VfktbiKiay9yq/BiPcNkZTfXiiFt1K7Eot32LpV4xFgEAoC+IsIDyJD/Chh3/yynqLGyeUgOPhcm+j4L29XSwzGVuYW7TefML2QcAgD4hwgLKk+wIG3FzavV80SG1x84g2ff/1H7LmuUXS3Pltuu5V/YBAKBPiLCA8iR7LKzPooZ5xSdY5G+z/sWnA17VT5Y0jY6w5gXabZJ9AADoEyIsoDzJjLBq/9Vt8kUn1MaLfXVMPfD+6ADHvFER1sJ55AXZBwCAPiHCAsqTzAgbuK2TbVRCtawz/VaE7NMSuL9fsTxR52gtnCd4fDJQFgAAPUCEBZQneRE25GDf6Lu18lQYe/HTGQfeXRxfvUD0Kdgyffa+ZFotAIBeIsICypOsCBt2ZoRT1EnWvI4Dj72XfZI6wGNBm1J5ok7Qlmy14Mo72Q0AgL4hwgLKk5wIG35lYtWokGpuVabj7PX7z1696+Nz/8al4zsXj+pcwyGfiLbWlbvNOu7HjLAAAD1GhAWUJxkRVnV3Vp2o6bRy5avZZdRfXVwaV69QxsHe0bFs1ZqNXLoMn7flnM87Rg8AAPQdERZQnqRHWPXjv5tZifda5G26VNdkBAAAKAIRFlCeJEdY9csN7QtGjSLIV2umFwkWAKBYRFhAeZIcYd/u6m5vHjXbQNUJVxjsCgBQLiIsoDxJjbBhxwcUj3pmQe7SI88w4SsAQMGIsIDyJDHChl8cW85CvNHSof/hWNNpAQCgKERYQHmSFmEjbkyraWFukSu3becdQbIPAABFIsICypOkCPtxOi2rViueMW8WAEDRiLCA8iQlwkbcnF7dUrzLIk/FCf9wKxcAQNmIsIDyJD7Cqp9t61oyd9R0WlER1oN7uQAAykaEBZQnERE29NqmiW4DXGuVyBudX6NbPvvqbXsOXXT8BcMJAAAKRYQFlCdpt3MBAGAwiLCA8hBhAQBGjggLKA8RFgBg5IiwgPIQYQEARo4ICygPERYAYOSIsIDyEGEBAEaOCAsoDxEWAGDkiLCA8hBhAQBGjggLKA8RFgBg5IiwgPIQYQEARo4ICygPERYAYOSIsIDyEGEBAEaOCAsoDxEWAGDkiLCA8hBhoVeC/1k7aVj/7u3bd2jn+tnWsfv8S2HyjYmnfnl66fihfbvF+F3tO/cYPG7JKfkSAMaBCAsoDxEW+ifCb1v3UrnNxZZpYVlzyIqNmzZEtY3r165dsXTBtNF9mzgVFotymedvtNhXJd+TVOG+GzuV0PyuXHmde66/HaSWSwAYDSIsoDxEWOgh1d059S2iYmW+mjO8ImSnluBby9oUz21ZY8oNHQsTSXV3dr3o32Vp33NvoOwEYFSIsIDyEGGhh94f6FfMXMRKi4pjLoXLvpjCr0yuUtbtdNKHEXz0fn+f6N+Vu3jfA8GyD4BxIcICykOEhf4J9xjrbJXL3MLccdCR97IvFpXPItd+e1MgcoZfGFku6nflsu66LUj2ATAyRFhAeYiw0DsRt2fWtBSbpYV15+1xXdlXv7p+7mYKDFuNuDG9WvTvyu+y1p9RsICRIsICykOEhb5RPVrUNG/U4NSCLVY8S+VUqfKe39gy6ncVaJz8O8MAKBURFlAeIiz0jPrFGpfoCQcs68+6FzNVvjmzesud5N/A9f/Uz1e2KhT9u+rO1HXbGADjQIQFlIcICz0TuKOTtaWIlXmcJ3jEuJUr7Lp7i07rXqbkedk3Wztai+3fIk+lSZd13zYGwBgQYQHlIcJCv4Qc6G+fW2yTeR0HHQ+VfULw3S0DK1o3WeiTklf7gw/0Kxr9u5zczqXA3AYAlIoICygPERZ6JezMsLLRDxrI69iwZ79+A/r169utg0udcnYW5hYW1ad5puTV/rDTQ8tE/a7cJQccjmPiAwBGgQgLKA8RFvok/Mr4itFTXOWr02/uogULFs2fN2fauIHNnAqm/LnS/36XuU2PnUynBRg1IiygPERY6BGV1+xa0VNcWTX7+7HWoNeIe/Pr5yvW+0BKbqaqu3M0v6ug68YUHWALQHGIsIDyEGGhP9S+S1tET6eVv968BzEGvYadHlq586YAWaUE9WP5uwo0+9uPBAsYNyIsoDxEWOgN9ct1bayjRhFY1p52K+ag15DdfTsuf5KCSVP9an276N+Vt+Hc+0wICxg5IiygPERY6I2gXV2KRE+nVXHiPzGnuFK/+efAab+PSVMdFhaWzDgbtLubTdTvsqg+PUVvEQOgRERYQHmIsNAX7w8NdEjIFFfht2e3H7rvnayS5v3RwY55xO+yKj/6EhPCAkaPCAsoDxEWeiLs7IjyUVNc5Sk14Gg8U1yF31ncsnyf/clLsOEXRjnnifpdToOOMyEsACIsoDxEWOiH8KsTKmmmuOq5K858qn5xfHT1Qo69knkONuLapCr5on5XEabTAiAQYQHlIcJCL0RcnVI5eoori0aLHum+vertzQ2Dqha2NHfou1crwYZeX+JSolD+Mp1X3JI9nxXmMcHZIup3WTZZ4stkBACIsIACEWGhB9TPd3QvFf1QLos85Qfv9QkKDY8W9u7Ni8e3Lx3ZNH9Mp1qOFlEvsLTvrT2KIPzcyApRQwLM85YYIrs+I+zBij8dzKN/l0WDhd7MRgCACAsoEBEW6SnCa9e0EYM71ikZPUVrAlruYjFHEaj9d/YqHnVK1arsKNkVB5XP/jmjh/Ru7myrycrRrYBj7Q69hszaH8eJXwDGgQgLKA8RFkoXtLdPCfM8ZYeclDUAJBIRFlAeIiwUTv18fTs7h46bfTmRCiCJiLCA8hBhoWTqV+fcW1Vs6n42gNuyACQZERZQHiIs0timTZvc3NxkkSyqhxsHd+w968gjpnYFkCxEWEB5iLBIM0FBQYMHD/7++++PHTsmuwBADxBhAeUhwiINREREzJ0796effqpVq9bt27dlLwDoByIsoDxEWKS2nTt35s+f387O7siRI7ILAPQJERZQHiIsUs/ly5ednZ1z5sy5fPlytZobrgDoKSIsoL/evn0rf4opnggb11uAz3r8+LGLi8t33303ZswYDpAA6DkiLKC/rl+/PmXKFFloiSvC3rx5c9KkSbIAEuzdu3fDhg379ttvO3To8OzZM9kLAHqMCAvotd9+++3TFKszwt64cSNHjhyHDh2SNZAAKpVq4cKFP//8c7Vq1Tw9PWUvAOg9Iiyg19q3b29iYtKxY0dZR/s0wor8Kl4mhIeHyy7gc/bt21e4cGFra+sDBw7ILgBQCCIsoNcWL16syaba52JjRdiP+VXQ9ADxu379epUqVX755RexgXHPFgAl4gsP0GtBQUEynGqlWO0IK/Jr9uzZNS+YP3++5gVAXJ4+ferq6vrtt9+6ubkFBwfLXgBQGiIsoO/KlSunSaiCJsV+jLCenp4f86vg4+OjeQvwKbHBjBw58rvvvmvbtq2fn5/sBQBlIsIC+m7ixIkyokYTKVYTYS9cuPAxv5qamubPn1++AYhJrVYvW7bs119/rVix4tWrV2UvACgZERbQd9euXdPk1I9Klijx2y+/ZsuWTdbR+vXrJ98AaDl8+LCNjU3BggX37NkjuwBA+YiwgAL8+uuvMqjGTSQV+Wog2s2bN2vUqJEjR4758+erVCrZCwAGgQgLKIBmaq14fP3110ynhY/8/f07d+6cJUuWIUOG8MA2AAaJCAsowJYtW2RWjUOdOnXkS2Hc3r9/P27cuO+//75Vq1a+vr6yFwAMDhEWUICgoKBMmTLJuKoL02khMjJy5cqVv//+e7ly5Tw8PGQvABgoIiygDNpTa33q0aNH8nUwSsePH7e3t7eystq+fbvsAgCDRoQFlCHW1FofmZqaFihQQL4IxsfLy6tu3bo//vjj7NmzIyIiZC8AGDoiLKAMn06t9RHTaRmnly9fdu/ePUuWLAMGDAgMDJS9AGAciLCAYvzyyy8ytMbEdFrGJiwsbNKkSVmzZm3WrJm3t7fsBQBjQoQFFEPn1FpMp2Vs1q1bZ25u7uTkdP78edkFAMaHCAsohs6pterWrSsXw9CdPn26ePHiFhYWmzdvll0AYKyIsIBi6Jxaa8GCBXIxDNf9+/cbNmyYLVu26dOnc9IdAAQiLKAkn06txXRahi0gIKBPnz5ZsmQR/xU/y14AMHpEWEBJtKfWYjotwxYeHj59+vRs2bI1atTo/v37shcAEI0ICyhJrKm1+vfvLxfAsGzatMnCwqJ48eKnT5+WXQAALURYQGG0p9Y6cuSI7IWhOH/+vJOTk7m5+bp16yIjI2UvACAmIiygMK6urpr8ynRaBsbb27tZs2ZZs2adPHlyWFiY7AUA6EKEBRTm49RaNWvWlF1QuMDAwAEDBmTJkqV79+4vX76UvQCAuBFhAYUJCgrSRNhZs2bJLihWRETE7Nmzf/zxx7p163p5ecleAMDnEGEB5TE1NRUR9t69e7KGMm3fvt3Kysre3v748eOyCwCQMAYSYa9eubJr504azUjany4uf/zxR6xOmoKa+/TphQsV+vHHH/v26bNzx45YS2k0Gu1je/jggcw6iMlAImz/vv0scpnTaEbSfvvl1++/+y5WJ00RLVfO37J8800GU9NsWbPm/v2PWEtpNBotVluyaLHMOojJoCLsvDlzN23YSKMZQ5s+bXqsHpqet5XLV9SvV//LL76sXKmy+E6KtZRGo9FitZ7duxNh42FQEfaF/wtZA4DeUKlU8+fPz5EjR40aNW7evCl7ASBeJ0+cIMLGgwgLAKloz549BQsWtLGxOXz4sOwCgAQgwsaPCAsAqeLq1auVKlX69ddfly1bplarZS8AJAwRNn5EWABIYX5+fm3atPnuu+9GjRoVEhIiewEgMYiw8SPCAkCKCQ4OdnNz+/bbb11dXZ8+fSp7ASDxiLDxI8ICQIqZOHFilSpVrl+/LmsASCoibPyIsACQYhjzCiClEGHjR4QFAADQO0TY+BFhAQAA9A4RNn5EWAAAAL1DhI2fQUXYcWPGLFqwgEaj0Wg0Gk3prXWLlkTYeBhUhKXRaDQajUZTbrM0z13KsXiNqtUaN2jYtFGj5k2aTpsy1dvbOzIyUiYe/MdAIuy9u/fGjh5TpGChA/v3nzl9mkaj0Wg0Gk0pbeXy5e3btiucv0CsRPux2VpbDxsy5Pq1azL3wGAibFBgkGYdjxg2XHYBAADot9u3bjWsV0+TYWwKW7d1cZk8ceK2rVsP7Nu/f+++zRs3jR87tnWLlh/TbeP6DW7dvCnfbNwMJMKOHjlKs2rz5s7j5eUlewEAAPRSRETE1MmTrfJYiPRSv07dLZs2hYaGymWfCA4O3rB+fd1atcWLxVsmjBv/4cMHucxYGUKEvXv3rlid1SpXOXXypFi14mBFLgAAANA/YWFhnTt0EKHFzrrIxvUbZO/nREZGihcXLWIj3tj2zz/jibzGwBAirOaWvbNnzoifu3TqJH4+eOCAZhEAAIBeEfnVpWUrEVeqV6n6/Plz2ZtgL1+8qFuzlnh74wYNg4ODZa/xUXyEPbD/gFiLIrlqSl9f3wJ5rZzLlgsPD9f0AAAA6I8xo6JGP9avUzcwMFB2JdK7d+9aNG0qPqR/336yy/goO8KK45jypcsUsLJ67PtYdv3775RJk8RKXTh/vqwBAAD0w/Fjx0VKKe7g8OrVK9mVJG/fvi3nVFp81M7tO2SXkVF2hJ0za7ZYedOnTpN1tODg4BIOxYoULPTyBQ/rAgAA+kKtVpcvXUZElxPHj8uuZLj8z2WrPBYOtnbGOShWwRH22bNnhfMXKF2i5Pv372XXf7Zs2iS2j0EDBsgaAAAgve3bu1fkkw7tXGWdbAP6RT3aac2q1bI2JgqOsL169BCrbdfOnbLWEhkZWb9OXUvz3J7Xr8suAACAdNW4QUMRXS6cPy/rZPPy8hIfWLF8BVkbE6VG2EsXL4p11qxxE1l/4vI/l8ULmjRsJGsAAID08/btW0vz3M7lyss6hTSu30AEHl9fX1kbDUVGWLVaXbtGjby588T/gIo+PXuJlbprh47TtAAAAGnp9KlTIpak+BwCE8dPEB+7Y9t2WRsNRUbYdWvWiLU1bMgQWcfh42BZI5/7FwAApLsF8+aJ9JLi41YP7NsvPnb0yFGyNhrKi7BBgUEOtnZ21kUCAgJkV9xmz5wp1utMd3dZAwAApIcJ48aLTJLiT1+6cjlq5GS/3n1kbTSUF2FHjRgpVtWK5ctlHa+w0LAypZwK5cvv5+cnuwAAANLchLHjRIA5cviwrFPI9WvXxMf26dlL1kZDYRHWy8vLKo9F9SpVVSqV7PqcPbt3i1Xbs3t3WQMAAKS5me7uIpBs2bxZ1ink5IkT4mM/O7rS8CgswrZu0VKsp7Nnzso6YZo1biLe5XHpkqwBAADSlmbQ6ii3EbJOIZrHPBnh1LBKirCadd+tcxdZJ9jNGzctzXPXrVkrMjJSdgEAAKQhf39/EWPq1a4j6xTi2qat+Nj452gySIqJsGGhYeWcShe0yvfk8WPZlRh/DRwkVvDG9RtkDQAAkLacy5W3NM/t7e0t62R79fJlASurokVsEj7A0mAoJsLOmTVLZFD3adNlnUhiHdsUtna0d3j37p3sAgAASEPLliwVYcZt2DBZJ9v0qdPEB06dPFnWxkQZEfbp06eF8uUvXbJUcmZ4XbxwkVjNE8dPkDUAAEAaCg4OtrW2LmiVz/vhQ9mVDM+fP7ezLlIgr9UL/xeyy5goI8L27N5dpM/du3bJOkk+fPhQsXyF/JZ5U/AEPgAAQMItWbRYRJq6tWqLWCK7kkStVrds1kx81JRJk2SXkVFAhL144YJYQy2aNpV1Mhw5fFh8VKf2HWQNAACQhiIjIzXTK/01cFBy7jIfN2aM+JAGdetFRETILiOj7xFWHGTUql49b+48t2/dkl3J06a1i1jlp0+dkjUAAEAa8vf3L12ipEgjfXr2SkIAFdFIc5O6o73Do0ePZK/x0fcIu3b1arGSUnDg872796zyWFSrVNkI790DAAD6wO/JE+dy5UXCaVS//t27d2VvAogY07RRY/HGMqWcUmRArXLpdYQNCgxysLUrWsTmzZs3sislaB5Ru3LFClkDAACkrZcvXjRp2EgEkvyWeSeMHffZ86kisI4bM0a8WLylYb16z549kwuMlV5H2FFuI1IjawYGBtrb2KZ4MgYAAEi4yMjINatW2xQqLNKOaK1btJw3Z+7xo8f8njx5/fr1q1evfHx8Dh44MHvmTM2ZV9FsCluvWrFSrVbLjzBi+hthvby8NGurrYuLa5u2Kds0n9yhneuHDx/CwsJCQ0Pfv38fHBz89u3boMAgEW2jNp2XL8URkr+/vzjQ8fPze/L4sa+vr9iYvL29xVYl/5QAAADJIELFogULNOMK4mkVy5efPWPmgwcPxOs/5pOnT5+KvPvY9/GjR49EPnkoFt+/f+/uPRGi7ty+fevmzRueNzyvX79+7drVK1cu/3PZw8Pj0sWLF86fP3/u3NkzZ8+cPn3q5EkReOQfRVHSOsIuWrCwRbNmndp3EPGxfdt27dq0EQn1z1atxZFH3Zq1qlWq3LxJU3Go0bhBw48HJXrYhv01RP7/AAAAoxQZGdmiabPKzs41qlarVrlK1YqVxM+VKjiLMFqhTNmiRWyKFbUvXaKkU/ESJYs5FndwEKVmeGTh/AUKWOUrUrCQ+KGgVb4Cea3yWVha5bGwNM8dK2+kTdu1c6f8X1KUNI2w4iihYL78VSpW6tm9e++ePfv07NWvd5/+ffsN7N9/YN9+dtZFSjkWHzZkiNuwYSOHu40Y7jZqxIgxI0eNGTW6aqXKYjWPHjFy8sRJUydPnjZlyvSp02ZMd581Y0bH9u3FKncbOmzBvHkLFyxYvHDR0sWLly1d+veyZZMnTMybO0/3rt3Wrl6zfu26DevWbdywYfPGTVs3b1m3dm2hfPlrVau+b+/eA/v2Hzxw4PChQ0ePHDl+9NjxY8fr16kr/iQnjh8/d/acOFK5eOGCx6VL/3j8c+Xy5eFDhua3zCuOXuT/EgAAMEqrV64SCaRfn76TJ06MDidT3adNn+keFU6GDx0qFnV0bb9w/vxFCxdGh5Mlfy9duvzvv0VpW9i6Xu0669asWS+SyfqoZLJl8+ZtW7du37atepWqIoHs2L5dk08O7D9w6ODBI4cPHztytG+v3iKBiBefOX367Jmz58+d0+QTDw+PFX8vF0l03py5np6eN2/cvHXz5p3bt728vO7dvXfx/HnrAgUHDxz46NEjEcP8njx5+vTp8+fPX/i/ePb0qfh19evUCQsLk/9LipKmEfbPVq3FAYrOvymRR0Wm1DmW+fSpU2LF7N2zR9Za/P39bQpbjx09WtZaVCpV3Vq1G9Wvr3O8iEjJ9ja2OscD7Ny+Q/w6EV5lreXB/fviUGnu7DmyBgAARkkEQZtChSeMHSdrLREREbVr1GjcoKHOaV+H/jXEwdbu9evXstayZdMmkUBEKpW1FhFGRX4VgVjWWkJDQ8uXLiPisqxj6tS+g1j6/v17WWsRnyY+U3yyrJUm7SKsOMIQK+bSxYuy1nL37l3xlyiOS2StRfyli7/6zh10P4ygc8eO5ZxK61wx4ohHfKbOiSrExiH+JGJDkbWWgICAYnZFhwwaLGstYkNs2qhxzWrVjXYOYQAAoNGhnWuFMmV1Pvd+wbx5BfJa3b93X9ZaLpw/LxLItq1bZa3l1cuXRYvY6JxFVCSQxvUbiFiscz5QEaNFmNY5QcHePXvEr9M5F76Pj09Bq3wz3d1lrUBpFGFfvXplb2M7bIiOIaRqtbpR/fp1a9bSuWLGjx1rU9j6+fPnstayf+8+sWJOnTwpay2+vr6F8uV3nzZd1lrCwsKqVKz4Z6vWso6pX+8+JRyKvQ0KkrWWNatW582d5/q1a7IGAABGafeuXSKBnDl9WtZavL29RTScPXOmrLWIBFKxfIW2Li6yjqlnt+6lS5R89+6drLWsXLHCKo/FDc8bstbief26CCcioshaS2BgoKO9Q/++/WQdU8tmzatVqhweHi5rBUqjCNurR49SjsXfvn0ray0rli8XK+bmjZuy1iLyolgxa1frWDFBgUEia/bv01fWMbm0bCVyqs4VM33qtML5Czz2fSxrLSeOHxdbpEjGstYiDm7EIc64MWNkDQAAjJImGg7s31/WWqJv8GparXKVDx8+yC4tUydPFgnkyWMdCUTzAPyjR47IWoufn1+RgoUmjp8gay0RERE1q1Vv2qixzhELgwYMKFbUXuf8oRvXb7A0z335n8uyVqa0iLDHjhwVK+bQwYOy1qJZMZMnTpS1Fs2Kada4ic4V89fAQXGtGDmUxMND1lq87tzJZ2G5ZNFiWWsJCQkp6+TUpVMnWcfU0bV9XENJAACA8RDhVURYEWRlrWX9unUiGl65rCMa3r51SySQZUuWylrLu3fvSpco2bNbd1nH5NqmrXPZcjpHLMybM7dAXqsH93WMWDh75ozIQjqnGnjh/8LOusgotxGyVqxUj7DBwcGlS5bq3qWrrGOKWjHlyoeF6rjBS7NiHj54IGst586eEytm5/YdstYSz1AStVrdoG69erXr6ByxMGbUKFtra39/f1lr2bN7t/h1OoeSAAAA43Hm9GkRCXbv2iVrLSJCiCAxeuQoWWsRCUTEj/p16sZ1i7mILjonZ43nFnMRkApY6b7FXORdkXo7tHOVdUzdOncRwUzEM1krVqpHWBHzRdh/+eKFrLV8dsXMmTVb1lpE3hWpV2RfWcckDmKcipfQOZTk76VLxQGQOAyStZarV66IwyZx8CRrLZrrBQP66R5KAgAAjET89/536dSpTCmnkJAQWWtZuniJSCB3bt+WtZbP3mL+18BBstYSGRnZrHGTGlWr6bzFfOL4CUUKFnr69KmstRw8cED8umNHjspayVI3wv7j8Y+IhhvXb5C1loSsGJ1DSSZNmChWjJ+fn6y1aIaSiP/KWsuTx48L5y8wdfJkWWsRv6V6laotmzWTdUya6wU8ihYAACMXz73/B/btFwnk+LHjstby2DcqgUybMlXWWuK/xbx/n74lHIoFBeq4xXzt6jhvMb/hecMqj4XOh/O/ffu2lGPx3j17ylrhUjHChoeHV61YqVXzFrKOKZ4Vs27NGhF8r129KmstN2/cFCtmxfLlstYS/1CSti4ulSronpJ2zqxZBaysvL29Za1Fc71AoU+tAAAAKcXT01OkxtUrV8lay9ugoJLFHPv26i3rmOKZFD+eW8xPnjghEojOW8yfP39uU9ha5y3mckra+g103kc0bEicU9IqUSpG2Jnu7gWt8ul8WkE8K+YzTyuoWUusGJ1DSUYMGx7XUJJtW7eKX3fxwgVZa7l/L+ppBQvmzZO1Fs31griGkgAAACOhiYZNGjbSGQ2HDBpczK5oQECArLXEMym+5hbzpYuXyFqL5hbzzh07yjqmpD2tQPwZxJ9k65Ytsla+1IqwmsdILFqwQNZa4l8x8TytYNHChXE+rcDDQ6yYzRt1DCURRxvimGPoXzqmpBUbotgcxUapcyhJPNcLAACA8YjnaQXnz0XdYr592zZZa9FMij986FBZa9HcYh7XDV5jR4+O6xbzzz6tYNaMGbLWEhYWVtnZuU1r3VPSKlSqRFgRDRvXb1CnRk2d9/6LFWNTWPeK2bd3r1gxJ0+ckLWWeB4jER4eXqVixdYtWso6pt49e8Y1Je3qlavy5s7j6ekpay2auYJXrVgpawAAYJQ0TyuYM2uWrLVobjFv16aNrGPq1aOHU/ESOhPIZ28xX7d2ray1xH+LedTTCuKYknbalKlxTUmrXKkSYTWPkdD5tIJrV6+KaKhzxWieVtCvdx9Zx9SqeYu4HiPhPm16oXz5dY5YOH70mMjEOqeklU83Hjde1loiIiJqVa8e1/UCAABgJEQSaNG0afUqVXVGw8kTJ1oXKOj35ImstWgmxT986JCstWhuMZ82ZYqstYjfUqNqNfEbdSaQQQMGxHWLeTxPK7hz+3ZcU9IqWspH2NevXxcpWGjSBB1PKxDq1KgZ14oZOdwtrqEkO7fviGvFiGOjqBELCxfKWovIu2VKOXXr3EXWMXXt1DmupxsvWbQ4rusFAADAeIgkKhLI1StXZK3lhf8LkRaWL1smay0i51SrVDmuSfH79+0X1y3m69eti7rF/OFDWWvx8vISmVjnlLRhoWHFitrH9bSCti4ucY1YULRUOQt75PBhnU8rEG543vDx8ZFFTM+fP79w/rwsYhJB8+CBA7KISWwlu3bu1DliQTh96pTYwmQR072793Q+P0MQxzc6D5sAAICxEdFF/vSJWzdvxhUN/Z480XmLuRAcHKzzkVqC+DSdU2Vp6EzSGndu347raQXijxFX9FK01LqdCwAAAEglRFgAAAAoDBEWAAAACkOEBQAAgMIQYQEAAPSLzifwQ5vhRNiWzZp7Xr8uC8DQTRw/gUdvAIBBCg0NLWBlJQvEwXAibP06deOaJAswPKNHjjK8eaoBAAIRNiEMP8I+ffpU/gQYkLgiLBs8AChdXBE2MjLy9OnTsjB6Bh5hBw4caGJiovPZs4Ci6Yywx44dExu8u7u7rAEACqQzwor8KvbwgqyNniFH2C1btmTKlGnpUi62wgB9GmE9PT1/++23Nm3ayBoAoEyfRtgPHz60bNnSysrq1atXssvoGWyEnT9/fs6cOa/E/Sg2QNFiRdiTJ0/myJFj/fr1sgYAKFasCPvu3bvKlSvXrVtX9MsuGGqEHTZsWN68eR8+fKgpAcOjHWG3bNki8uvhw4c1JQBA0bQjrL+/v729fadOnVQqlaYHGoYWYcUKbt++fbFixV68eCEXAIboY4TlggMAGJiPEfb+/fuWlpYjR47U9EObQUXYc2fP1q5du1q1asHBwbIXMFCaCMsFBwAwPJoI6+Hh8csvvyxcuFD2IiblRdgTx48fP6ajlXMqbWFhUaVKlcOHD8daRKMZXuvQztXWxiZfvnzbt22PtYhGo9Foim4HDxz87ddfv//++7FjxsZapGnv37+XqciIKS/CtnVxadM6dmvcoOEXX3yR1zLvn61ax1pEoxlea9W8RbYffsieLVvLZs1jLaLRaDSa0lvpUqUyZshQvWq1WP0f2/Pnz2UqMmKGMJDA09Pz999/ty5c+NN5YQHD8/r161KlStnY2CxeuEh2AQAMxbRp00SqyZMrl6wRB8VH2JMnT/7vf/9bv379p/PCAobH19e3QIECAwcOHDViZKx5YQEAihYZGdm3b99ChQrdu3cv1ryw+JSyI6xmLqEjR46In4mwMHiaCw4zZswQP3/6aAMAgHJpHl5QunTpgICAWPPCQicFR9hYcwkRYWHYPl5w0JREWAAwGLEeXkCETQilRthP5xIiwsKAaV9w0CDCAoBh+PThBUTYhFBehNU8vMDR0THWwwuIsCkpIuj5Ay+ve48D3qtlT9KFBz6+d9fnRTBPFUkqnQ8vIMICgAHQ+fACImxCKC/Curi46Hx4ARE2RajfXFk7tLmztaVFLnPRLPM51u026/jjCLk4cYK9doxvV8E6f377Ug6FCtvX6DbjyJNwuQwJJPKrzocXEGEBQOlEVDU3N//04QVE2IRQXoS9detWRISOREWETT61/yG3KgWiwqt5bsvoCKtpBcr23e2XyNOx6penJtazy12oxqAdD6ImYA65v2Nw9XyWJdssux01zgcJ9fTpU51PSybCwripQl49unPt8u1nYbIjwcIDfG7dvP/8HReGoBeePXsmf9JChE0IpY6F/RQRNrlUPmvbODjWH7L86J0XwSrVO98L64Y3ss0TnWLzlOqzP1C+LiEi7q1wcTA3t2604O7/H26EXZlUPV8uywoDDrxM/vAEo0eEhTFSv7m5c0a/Fs6Ff8mW07pC/ba9Zx57kfDdybsb6wbUyJc18zc5cv787Tf/s2s0arcPF4agj4iwCUGEhYY6YHefup3W3ot5ivTt2dHVLKJHFBTptT/BT7NT+a5uWyS3hblD371Bsiua+smK1vnNLfI4DT6cmDwMXYiwMDZvry7rUvJnswxZrOqP3HIjILEXhvwPDi7xQ8Zvi3RYeydE1MG313Ww/trs1xozr3FhCHqHCJsQRFhoqB4c2H7pzSdfCmq/5c2sok7E5nVZFyD7Puf9+ZHlraJSb/ddMRJsVLZd3Civ+LSCDebf5yJe8hBhYUzCvFa7Fv42g+mXlk0XXY99J0QCRNyaU/XHDKZZyky++f8XhkLP/2VjZmpm4brtOReGoF+IsAlBhEX8wo4PKp43l7lVuVEXE3jBLfjgAIfcIqfma7DwUeycGu4x1lmkWwuLatOuJ+0WMUhEWBiNUM+5dXKamZp+VaDDVt+kHPyqHi6oni2DSYafW215I7uiqR/NqfSNqWmmXK67EnqEDqQJImxCEGERv5C93YtY5spXd4bWuYt4hR0dVCq3uUWu3MX6Hvz0Pos369sWjBpca1l/1j3OwyYHERbGQe27vlVuM1OTDNkqunsm+tataCHHe+XNZCI+oen6WEOYVA+nlslsamL6ldOk2+yQoEeIsAlBhEW83p8a7mRhXXOqR0Iv3UXcmlYjX3RIrTlVR+oNOzY4OuCaW3fY8lb2ISmIsDAGEbdnV86WQcTPrJWSfNT7bkebX8RHmJg5TXnwyYWhM/3yiXRrYlZkmAcXhqA/iLAJQYRFPMJuzW1SrFzvHY8Svm9/v6ebddTY2Vx5W63w/3R8WcTVSZr7w/JVm3qDb4xkIMLC8KkeLqyZPSp9ZrLqcyqpN12F7m2fM6P4jAw5/tzx6We8XlrzK9OoDOs49hbnYaE3iLAJYTgRdu7sOb6+vrJA8kU8Pzu7TWnL/NV6rfTwT3DYVD9e0jjqhi2LXAW6bNMxhUHEbfe60RE2r+Og40m7KIhou3buPH3qlCwAQ/TucFeLqFOkpl+XmeqV1HwZcdXNzkx8iImZzfArOi4M7esQHXBNs9RbyUQp0BsREREL5s2TBeJgOBEWKeW998l1s4a0LV84b1QSjT5jat90+umETb4YcXtmzegne5nb9T+gI6Kq7s6up5mly7rnPqayARAX9YtVDX6IOgVr+nXluU9Uob7nt8wbPaBbB9dOvd3m7Lj+KoGZNmRj06xRp1lNMlec81THhaGLfxWWCXfYZS4MAUpChEUs6sDLWxdMmzC8a9OK/z1mNipx2rRcrvWUgjhFXJ9WVRNhHQYd0RFhI2671/k40SwRFkBcXvxd57uo7Gma2cblr7Ylc2bNYVGoQO7sX2aI7jT7ueyg3X6fj7Fq72llM0dFVNNvGqyJmg82lojrI+2jI2zGnO33cmEIUBIiLOL27t6+KS5l8mpSbG7Hnns+f51N5TW7ljwL22+/rgh7bUp1BhIA+JyQ7X/miDoHa2KaIWvhxqP/e5ZB+NPjk2r9ZhYdY7+xG3j8czuljxE1w48u23UcNatujS2mWf5Di80cVQNKQoRF/CJ81ncoET2HgKVt7wM6TmLEpH65pqVmLGyh7jt1jIUNO+tWLvrT8lXldi4AcVHdGucoo2XTDbFmL3l7rHcBTYjNbDvcI/4JqyM8hhbRfM7P7XbriKgR10cU1SzP1nILERZQEiIsPkd1f2GDfFGp1KLRok+eVfCJsBODox6FYJErb+sVOobPhu3rbW8eFWELtlnPXOIA4hB2uMsfUbdZmWQqMOBc7JSq9ltaO2v0KdqMeXoci/dyjurGaAd5Frb1Nl0R9tJQ6+jlDCQAlIYIi89SP1ncNH9UhG24yOezEVb9YrVLoagIa1l98rVPTrOq/Zc3j/qoqFljOQkLIC6hO9poxhGYOU3W8TjqVyvqfR99k5ZZyYnxTlag9p9fWTMW9tvG63VcRgo71j13dFQ2K8LtXICyEGHxeWFHBpYwN7e07r7nswMJ/v1X9WB+4+jhszYdt37y8IJwjzEVoh4wm7vMyDOc8AAQl7CDnX6Pjpa6JxJQ3RlfIvrkaaa8fU7FO5QgbH/H6FmzTDJXmvvs0w8K3doqeuZZ069qLHkl+wAoAhEWn/d+b8+i5uY2bdbpeFbBp1SPljXNLyKsVblRF2N9taifr2xuFbWo/OhLJFgAcVLdlLdZ6T4L+2/IukbfRp2GzVRw0Pl4I6z62YKqX0e90sx6yKVPLww9ne0cfZLWzGY4J2EBZSHC4rPenx1W1iqP0+AjQbLjo3C/c+vmzVtxzCfmELM3e/tEPUXWourUWEMJAnf2tMltkatgiyXenx2RAMCY/fdQrYy/ddQ1uUnojj9/yhB18rTaws8cWqu8JpX6QmRY0+/qr/pk/oLw032top6ekDFP96McVgPKQoSFFPzg7P59p++8jH0iIuTKjEaF8joPOPDJzVlhHmMrRg9stSgz6Fiw7Iymerylo4NlLouKI89pzUqg9lvzp625ecHqk//RMVcBAGh5t6PNr1EZ1sx+5PVPz48Gr2mQxdTE9ItSkz773C7VA/fyUedhM+XtfTL2hSG/ORWj4m3UA2xJsIDCEGERLfTwIIfcFrnMLQtVcB277rxvcFRejXh1Y9uYZvYFnbuv99JxK2/QFtcC4i2i5W+21C9mwFX77R5Y3sqyUD13j3eannc3lrk65LEq1mbFHb4qAHxW+IUhRTKLmJrZYbRn7AwbcWmItZlJhh9qL32ivesJ9z22eML4uXvvx9xhvd7SOuqM7qdDCd6sb5o9g4lplvLu97gwBCgNERbRVPdXt3GIutFKNouC1vb2BfLZVWwzdsP1wDiu0wVdmPWnk3URxyaTjuqYPyvi8f5xjYsXKFi+dd9hw3o2LZ8/n1OzMXsekF8BJMzrvZ3zmpmaZMzluivmHHwhp/sVyJThu9ITr2mfVg073b+AyLwmpplyddgrj501VD7L6/+cwSSTVa/jWvekqh8trJ41g+lXtkPOJuBOVQB6hgiL/0S8uHFo47LZ06dNmjxt+txl63advumf7LwZ7H1mx5oFM2bMWbHb4wnDBwAkyttzY8r8kMHUzMJlg89/J1DV/gf72H6T2aL1+lgTVb9ZXuebqBu3TEwzV5jxKNaFId+Nba2+zPBdCbezcqaUt1dm1vwlY6Zfa8z25MAaUCIiLABAb6kDLsxsVjBrRrMcji0GjJ06ZbhrBfMf89Yetd/30wGyb06OqZL7+29zlh2097mOC0M+W/uWzfnlt3kr/dm1c8vyeb756rcKfTfqGiQFQAmIsAAA/Rbx4vrev93HurmNmTJ39cFbAUkfuPru3tG18yePGjF+zoYzjxg+ACgZERYAAAAKQ4QFAACAwhBhAQAAoDBEWAAAACgMERYAAAAKQ4QFAACAwhBhAQAAoDBEWAAAACgMERYAAAAKQ4QFAACAwhBhAQAAoDBEWAAAACgMERYAAAAKQ4QFAACAwhBhAQAAoDBEWAAAACgMERYAAAAKQ4QFAACAwhBhAQAAoDBEWAAAACgMERYAAAAKQ4QFAACAwhBhAQAAoDBEWAAAACgMERYAAAAKQ4QFAACAwhBhAQAAoDBEWAAAACgMERYAAAAKQ4QFAACAwhBhAQAAoDBEWAAAACgMERYAAAAKQ4QFAACAwhBhAQAAoDBEWAAAACgMERYAAAAKQ4QFAACAwhBhAQAAoDBEWAAAACgMERYAAAAKQ4QFAACAwhBhAQAAoDBEWAAAACgMERYAAAAKQ4QFAACAwhBhAQAAoDBEWAAAACgMERYAAAAKQ4QFAACAwhBhAQAAoDBEWAAAACgMERYAAAAKQ4QFAACAwhBhAQAAoDBEWAAAACgMERYAAAAKQ4QFAACAwhBhAQAAoDBEWAAAACgMERYAAAAKQ4QFAACAwhBhAQAAoDBEWAAAACgMERYAAAAKQ4QFAACAwhBhAQAAoDBEWAAAACgMERYAAAAKQ4QFAACAwhBhAQAAoDBEWAAAACgMERYAAAAKQ4QFAACAwhBhAQAAoDBEWAAAACgMERYAAAAKQ4QFAACAwhBhAQAAoDBEWAAAACgMERYAAAAKQ4QFAACAwhBhAQAAoDBEWAAAACgMERYAAAAKQ4QFAACAwhBhAQAAoDBEWAAAACgMERYwMKF3NvYtn/PL7/PX6PjXmJHdq9lWnHhxf9dcGU1MzOzcrkXIVwEAoGREWMCAqF+dGFXhf2ZZrDus8wrRdIVfHlHLuVzeTCYmmfL2ORWu6QQAQNmIsIDBeHdhfPnsGczMW673VcsuIWyv668ZTUxMMv7qukfGWgAAFM7gI+y7+4eXjhvQrWufMasvvVb/qwq4d3rn8hljl52TywEDoX61t7OVmamZZed9AbJLI2xfh5wiwmb4ofG6QNkFAIDCGXKEjX9IoHwRYBgCD3axzGSa4eem6/y1zsBGeb205lemJqbfVF/0THuJ+s3VNcPb1K5QplyVxt2nH/RhiAEAQEEMNcIyJBBGRWzd9l+ammYuOuJq7Pu1Qvd3/D2jiWnm0lMeqGSXEHjarVROc4eKVcoX+TUq4Jrldtn0NFb2BQAoXpxXo4PlCxTLMCMsQwJhXN5sdRHbtmmWynMfxY6hIQc7RV95sHXTCrehZ0f/2X/nI01H4KVJFbNnMDWzH+3JdAUAYDgMfIIaA4ywDAmEsXm1sn7WDCam31RdEPtEqvrJsjrZMpiYZLLsffL/rzyoX9+4LvNrtNCofxoZc3c/GiY7AADKZgRXow0vwiZ6SKCgenR44dTJH01deFguABTg3eaW2UVMNXMYc1NrqECUgH2dxL+GqCsP7XbHc+Uh/Ew/K7OcbXdyaAcABsEorkYbWoRN9JDAKO/2d8odtU6ljLk67pNLAP0XcWW4jZmJiem3jdbF2CWp/Xd1KvB1VILNkLXx2jeyV4eI66NLWrVYq72fAwAolbFcjTawCJvYIYFRVA9mVbWrP2ySPAU7ecrcvbEzLqDHwvZ3/E1s2qZZm27UirCh12bUdXZp7pDZ1MT0q6rzn6n/VQc8fPDik007zHtbz2JWDZc/ZBwsABiCpFyN/k/Iw6NLRndv27ZLvzGbb+v714JhRdjEDgmMEnykR7H6S59wAgpKFX6yd9TQJpOMudrueBW9Iav8T4yv6VB//sWVjbOaiuM2+1GeERH3l7WsPfzMu+i3aLw9N8e1Qp4sGcRLTDPlKD/6VDxnagEASpCkq9FRIh7tGlLZIlepbksv+CvjnIZBRdgkDAlUec+r8sMvjs17jf376IN35FgoUcCGpj+KDd/EJMN3BWq06+hS3TpHzkoTzweGewy1NhPdZg79185tW6HBHM9PbtdSB/ueXz+qccHvxKGfmVXvE6GyHwCgREm5Gi0EX55Z54+v/6i/6IaCxsgaUoRNwpDAkBO9raIWRDE1+8Wp26obip8nDcYn3HNurZxmmg3ZNPMvTt3X3o76JxBx1c0uKsKKnZlVk7lX4t601a92t7fIZJopb++TsgcAoEBJuRr9r8pnVZPfzb60HXjqrexRBkOKsEkYEhj+6v7Vs4e3LJ3Yp2HRHCICmGb8qcLEC6RYKE7EkzNr3MeNn7nysNf/jxVQv7q4atLYacuP3P/cYbXq9jhHsww52uyUNQBAeZIyQY3Ke3GtHzNksux6SFkB1rAibJKHBEZTvzrnXt88s6lp5iJ/nWN6TBiZ0K2tsmUuOvK6LAEAipOUCWpCDnfNk9H0i5ITbovQqw4LDlHOvb0GNRY2GUMCNUKvji/9nWnG3zrulx2AcVB5TSr1ffGxPJ4LAJQrCVej325plSODqVlhl5H9GpYrYp7VzDTzT7ZNJp98oYC7gwwqwiZ7SKAc7Jy5/AxZAoYo/M6m0QNHLT3lJ8dDvb06u06B0qPPM4QGABQs8Vejw451z53RJEO24t2WXYyahyD0webO1l+ZZshabvJ1vX98l2FFWCGZQwL/fbeq/jdfVlkgK8AAqf3XNvlfRlPTDFnMS9Zq3KhWeaeqHRZcDFDAITcAIB6Jvhr9enH1L0xNv6i26JWmFkKO9RA5OMP/Wm/T97GxBhdhk+vN37W/L+p2VVaAYYrw9zy6ff2aNRu2Hbzw4E2sUf8AAGVK5NVote/MCplNMmRruUVrSsWoZ45nMslUYMA52aGviLAxqJ+uaFCw0Uo/zkcBAADlSdTV6Lcr639jmiF7q63as4IH/l37K9OMubsflbW+MuoIG3FjfosyFVpP2OcdverCvXcNrlK201YCLAAAMHyqm2OKmZmalZnmrRV9QjY0+c40c7np3rLWV0YdYVW3ZlX5MaOpiemXP1nZFClYuFwb95PPya8AAMAoqLyml8uS4btaS559jD9q7+llM2epMEPXU2j1irEPJIh4cePotvXrt+47c+c18wkBAACjEnLOrViWL+2GnpdDDMKujSnxUzG3c5+9/z3dMRYWAADAaKl8d/QpmTNP1UEL1q+d17928cqDdj1Swm2+RFgAAACjpg68f3rXxo3bDl99qpgHlBJhAQAAoDBEWAAAACgMERYAAAAKQ4QFAACAwhBhAQAAoDBEWAAAACgMERZQpMjISPkTAMAIREREhIUqZsarNGBQETY0NDQgIEAWgOES+XXRwoWyAAAYgcOHDu3Ytl0WMLAIu3XLliWLFssCMFxnz5wpnL/A27dvZQ0AMHQd2rm2aNpUFjCwCNu0UePKzs5cYIXB696lq0Uu89UrV8kaAGDQnj17ZmmeW+z5vR8+lF1Gz3Ai7P1798WqFe3SxYuyCzBEL/xfWOWxEJt69SpVOWADAGMwZ9YsTciZOH6C7DJ6hhNhx40Zo1m7/fv0lV2AIZo3Z65mUxftH49/ZC8AwECp1eqyTk6a3X4xu6Lh4eFygXEzkAgbFhZmb2OrWbsFrfIFBQbJBYBhUalUH3dkonHABgAG7+SJEx93+6Lt3bNHLjBuBhJhd+3cqb12V65YIRcAhuX40WPam3qBvFZv3ryRywAAhqhb5y7ae/4/W7WWC4ybgUTYVs1baK/dmtWqM0YQBqmja3vtTV20ZUuWymUAAIPz6uXLfBaWsfb8j30fy8VGzBAirI+PT6xVK9q1q1flYsBQPH36VHNHqnarVIFZOADAYC1asCDWbl+0aVOmysVGzBAi7JRJk2KtWtGGDBosFwOGYtWKlWWdnEQrXaJkkYKFND+L5nn9unwFAMCwtGntotnVFy1iY2ddRPNz4wYNOXlhCBHWbdiwzh07ilatchXncuU1P/fv01etVstXAIblse/jMqWcZAEAMALz5sydPHGiLGAwY2E1Fi1YMGHsOFkAhosICwDGhggbCxEWUB4iLAAYGyJsLERYQHmIsABgbIiwsRBhAeUhwgKAsSHCxkKEBZSHCAsAxoYIGwsRFlAeIiwAGBsibCxEWEB5iLAAYGyIsLEQYQHlIcICgLEhwsZChAWUhwgLAMaGCBsLERZQHiIsABgbImwsRFhAeYiwAGBsiLCxEGEB5SHCAoCxIcLGQoQFlIcICwDGhggbCxEWUB4iLAAYGyJsLERYQHmIsABgbJQSYd/dP7x03IBuXfuMWX3ptfpfVcC90zuXzxi77FywfEFKIcICyqP3ETbOPZhcDgBIJAVE2NA7G/uWz/nl9/lrdPxrzMju1WwrTry4v2uujCYmZnZu1yLkq1IKETatpdnRCQyYPkfY+Pdg8kUAgETS8wirfnViVIX/mWWx7rDOK0TTFX55RC3ncnkzmZhkytvnVLimM+UQYdNQ2h6dwABERkaKtHri+PEVy5eLnZf7tOkTx08YO3r0kEGDbQoVFj2bN246eeKE1507b968ke9JT2m/BwMAQxMWFib26vv37lu0cOFMd/epkyeLbDNm1KimjRo1rFtv7uw5mzZsPH702M0bN1++eCG+JuTb0te7C+PLZ89gZt5yva9adglhe11/FSHHJOOvrnvkl0IKUl6E9ff3P3f23Lo1a8aNGdOtc5cO7VzbtHZp2axZ8yZNK5QpW9apdM/u3cV3/ML587ds3nzq5Em/J0/kO9MV3+1IiGfPnu3asVNE1Z7dutesVr1QvvxOxUu0btHSbdiwKZMmzZoxY8G8eUsXLxE/21oXmTRhYr/efVo1b1G1YiVba2vRxD8EsacTn+Dt7Z32+7X02IMBgOJ5enquWL58lNsIkWfKOjkVsLKq7Ozc0bW9yDnTp06bM2v2ogULxJ6/7Z9/iqgj9v8D+vUTr6xRtVoxu6I2hQo3adho1IiRIvOI4KtWa+1+04z61d7OVmamZpad9wXILo2wfR1yii+ADD80Xhcou1KQAiKs+DJesmhxrx496tasVaRgoWJF7cXaGjxgoAipu3ftOnTw4PFjx8+eOXP+3Lkhgwd37dx5x7bt4vXii7xvr94tmzUv7uDgYGvn0rKV+L4Xr3/16pX83LTEdzvi5eXlJXZS9WrXsbex7dqps9hnic3Y8/r14GDd40t0DiR4/fr1yRMn5s2ZKz5B7ARLOBT7a+Ag8Q/k/fv38hWpKZ32YACgSOHh4WKP7TZsmFPxEhXLVxg2ZMiyJUuPHTkqMo9KpZIviknnQILAwEARgRYtXChikvgcEWr79+m7Z/fut2/fylekvsCDXSwzmWb4uek6/1gB+vXSml+Zmph+U33Rs0+idbjfqYV9mg7fFyrrxNPTCBsZGXnl8mVxqFG1YiXxTTz0ryHbtm69euVKUGCQfIUucQ0k8Pf3P3rkyJxZszq172Brbd2gbj3x880bN+Xi1MZ3O3QRG/mlixfHjx3rXLacyKPiGFrshiIiEjSgJCFjYR89evT30qXiKE4co7dv204cv3348EEuS3mJ3oOpnp9d/JdLrQplylVu2HHshutvPtm9AYDBCQ4O3rFte4+u3UQaaVy/wcL58x/cvy+XfU5CxsL6+fmtXrmq7Z9/ij2/S8tW27dtCwsLk8tSSfjlEfZfmppmLjriauwvsND9HX/PaGKaufSUB9qpPCq89q1h9W0GU9Mv661K+ik8vYuw58+dGzr4LxFbq1WqPHXyZBFbE349NCFjYcW3+JnTp8eMGiVyQ+mSpWZMd3/27JlcljqSdnQCAyaOsMUhmTg8q16lqtgCb3jekAsSLFG3c70NChJ7zJbNmhV3cJg2ZUpqDK1J7B5M5b2mpcUXmb7OnjPnD19kMDUxMc1i3XXHU/4ZADBYAQEB06ZMdbC169DOdeP6Da9evpQLEiwhEfajkJCQfXv3tmntUqyovYhGPj4+ckFKe7PV5Vexk89See6j2PvwkIOdom/3sXWL+dUQ5vfg0buAHW1/y2gwEfbokSON6tevVMF5yaLFSfu7TuztXHdu3x4xbHjRIjad2nc4fux4qowgSfzRiRa1/64BlUt3WkvCNRTiaHjt6tXlS5dp0bTp6VOnZG/iJW1Ggvv37o8eOcrexrZzx45ed+7I3hSQ2D3Y680uNs5/bb39Tvysfnv97zaFvjE1yZizzXZ9uCUNAFLWs2fPxowaJcLG0MF/PXr0SPYmXqIi7EfiN04cP0EE2fZt26XC9edXK+tnzWBi+k3VBbHPQqifLKuTLYOJSSbL3id13O6jujeplJnSI6wIjrt37apZrXqt6tX37N6dnByZ2AirIY5U1q9bV7dmrWqVKh/Yt1/2ppAkHJ18pH66qdUfmUwzl53uTYRVvPfv3y9dvKSUY/F2bdp4eHjI3qRKWoTVEH+SZUuWOto79O3V29fXV/YmR2L3YO829eu55YXWS8Mvu9mZmWbM2WFfKl/vAoC05OPjM2TQYBFex44e/fz5c9mbVEmLsBphYWErli8v4VCsZ7fu3g8fyt7ke7e5ZXaxkzdzGHMz1sm4gH2dLDOZRt3u0263rpSq9nEvl1m5EVak1c0bN1UsX6Fx/QbHjhyVvcmQtAj7kfgz1K5Ro17tOidPnJBdyZXko5OoF6xpVvjnH82IsAZAHBqVLObYvUvXlDoCTk6E1QgODp41Y4a9je2IYcPjH2L+WYneg4X4PHwW85Xhp/rkzZQhR5sdSR/WDwB65MOHD9OnTnOwtRP/DQiIeSNMUiUnwmq8f/9+/tx5xeyKDh38V2BgCtyFE3FluI2ZiYnpt43WxUiiav9dnQp8HbX/z5C18VqdF9jUj5QbYcVBQOMGDZs0bHTubIo9sCeZEVaIjIzcvWtXpQrOrVu0TIGBI0k/OlH5LG9YtMmSGQ2/JcIq2ps3bzR3iSb/zKu25EdYDbFj1dwPe/jQIdmVaMnYg30UfrqvVaavKsxgSwdgAG7euFmzWvUO7Vz9/f1lV0pIfoTVeBsUNGrEyJLFHJN/5Tlsf8ffMoovgKxNN2p9AYRem1HX2aW5Q2ZTE9Ovqs5/pv5XHfDwwYuYQUiZEVbExL+XLhWHJsv//jvht2olRPIjrIZKpVqyaLH4E4r/JmdgQ5K/21UPFtW1/3PTs+D1jYmwCnbo4EGxjxg7enRoaAqfXUypCKtx8cIF53LlRdRO0qmCZOzB/qP2mVH+qx/rr+B+LgDK9uHDhxnT3YsVtd+yebPsSjkpFWE1PC5dqlTBuXuXrkm4seyj8JO9o2a3N8mYq+2OV9F7cJX/ifE1HerPv7iycVYRc8zsR3lGRNxf1rL28DNRtz/8PwVG2EePHjVv0rRJw0apcXNcSkVYDfEnbNG0aYO69e7evSu7EimJ3+0Rd+bULNZh5wv1vyFEWIUKDAzs26u3c9lyly5elF0pKmUjrCBC9vixY0s4FEv8VZFk7MGkiGsjHbKVHHeVx3sAULLbt27VrlHDtU3b5A971SllI6wQFhomPjBJe/7/BGxo+mMG8Q1gkuG7AjXadXSpbp0jZ6WJ5wPDPYZam4luM4f+a+e2rdBgjmfsWx2UFmFXr1zlYGu3dPGSVLn3P6UjrBAZGbl29epidkV37dgpuxIjSd/tETfdq5XocTD6bBgRVpHu3L5dumSpUSNGpt4zBVI8wmqcPXOmuIPD8mXLZJ0wSd+DRVN5L6ljWX7SVe7kAqBgu3ftEmlh04aNsk4FKR5hNc6cPp2EPf9/wj3n1sppJhKNYJr5F6fua29HZdKIq252UV8AJqZZrJrMvaLjKT3KibAqlWro4L/q1KiZ8Cl8kyDFI6yGSCQVypQVnxzXAzPilPjv9vBrkyo5DTghn6lBhFWeC+fPFytqv2tnUo55Ei6VIqwgPrlmter9+/YTR+ey67OSvAcTIm7Pre3Qco1PIv9pAYAeWf73307FS4i0IOvUkUoRVhB7/lrVq/fv0zcRe/7/F/HkzBr3ceNnrjzs9f9X2tSvLq6aNHba8iP344ioComw4m+kc8eOf7ZqHdfTMlNKKkVYITAwsE1rl9YtWr55k6iZKxP53R7qMca5/PBzH1cnEVZh9u3dK/Lr2TNnZJ1qUi/CCu/fv+/ZvXuj+vXfBiV4poIk7sFeHx5QqfYUj9TdLwBAapoyaVKlCs5PHj+WdapJvQgrhIaG9urRo3GDhmn1ZFolRFjxLdiscZPePXum5sMtpdSLsIJarRYfXq1S5ZcvXsiuBEn4d3uEx8hiv5XrNWPeR7M6Onxpmsmq6fi58xfvv8d5Kv22asXKUo7Fb91MiwcXp2qE1RgzalTdWrVTZNaVOAR7TK1fbfARzRgbAFCciIiIAf36NahbL6WmzYpfqkZYITIycuRwt/p16iZzpsWE+W9e2JVJP4mRuhHW39+/epWq4rswZWceiEuqRliNObNmi4Ot1Bmp/W55nS+iz9fqYPpNgzVJP1BBqps6ebLYMESylHUqS4MIK0wYN75W9eqvX7+WdUoKuT63caUuW59oH5iFe63/++BrEi0AJXj//r1rm7bt2rRJvdseYkntCKsxfuzY2jVqpH4oV90aW8zM5IsqCxJ1WjCGVIywPj4+5ZxKL5g3T9apLw0irLBo4cIKZcqmwoPm1YGPbt2I4bJ79W9MzYoNOeJ585ZvIF/t+mrWjBl1a9VOnainW9pEWGHalKnVKldJ5PiZzwq+MqvWb1kLVGvd5qPWzeqWs/q14oyHXGwAoP8iIyM7d+jQp2eviAjdz9dMDWkTYYWpkydXr1I19c7Fqrz3z3brUvEPM1MT06/z1ek9evm5gKREnNSKsOL/vGL5CmtWrZZ1mkibCCssX7asfOkyyZlKLWEYC6sAu3ftKuvklPobQwxpFmGFCePGN2/SNOUGAqkeLKn7c0bNAPEYTL+tvtCPTR2AAkyZNClFd4wJkmYRVhg9clTrFi3TMqAnQapEWJVK5dKy1ZhRo2SdVtIswgru06Y3ql8/SffuJRwRVt9du3q1WFH71L4L9VNpGWGjTjZ07DhowABZA4Bx27Z1a4UyZdNm/Ku2tIywarXatU3boX8NkbVeSpUIO2rEyLYuLomegirZ0jLCCj27d+/Vo4csUgURVq89ffq0lGPxZDyaNenSMsIK79+/r1Oj5sL582UNAMbq8j+XixW1T/Izj5IjLSOsEBwcXKNqtaWLl8ha/6R8hF23dm1lZ+dETMeTctI4woaFhjWsV2/GdHdZw5iEhITUrlFj0cKFsk5baRxhhWfPnjkVL3Hq5ElZA4Dx8fPzK1nM8diRo7JOW2kcYQXx/1vCodjFCxdkrWdSOMJeOH/e0d7B29tb1mkrjSOs8OrlS/G9ngbzgELfdO/SNR2vrad9hBXOnD5dukTJNJlsBQD0zocPH+rUqLlk0WJZp7m0j7DC0SNHyjmVfvdO5+PB01lKRtjXr1+LtC6+52Sd5tI+wgonjh8vXbJUupx1RnrZv3df1YqVwsPT7aH+6RJhhVEjRvbu2VMWAGBMFsyb59qmrSzSQ7pEWGHIoMED+/eXhT5JyQgr/g/Hjh4ti/SQLhFWGD50aP8+fWUBQycORkuXKHnp4kVZp4f0irChoaGVnZ1379olawAwDk8eP3awtfP19ZV1ekivCBsSElKhTNmDBw7IWm+kWIT18PBwKl4ifU81p1eEff/+vXO58vv37pM1DNrokaMGDxgoi3SSXhFWuH7tmqO9A8MJABiVjq7t586eI4t0kl4RVvC4dKmUY3GRZWWtH1ImwqpUqhpVq6X7uZn0irCCWLulS5QMDQ2VNQyUp6dncQeHlJ7qP9HSMcIKQwYNnjBuvCwAwNAdPHCgsrNzOg4e00jHCCv06tFjprt+3b+eMhF26eIlLi1bySL9pGOEFbp17iI2L1nAEKnV6rq1am/ZvFnW6Sd9I+wL/xf2NrZPHqfR03QBIB2FhISI/e3ZM2dlnX7SN8KKfb7Y8/v7+8taD6RAhH3+/LmDrZ33w4eyTj/pG2F9fHzE38OrV69kDYOzcsWKls2aySJdpW+EFWZMd+e+LgDGQKTGvr16yyJdpW+EFSaOn5Du4+i0pUCE7dOz17QpU2WRrtI3wgqjR45yGzZMFjAsYWFhJRyKpf2DuHRK9wgbEhJSspjjrZs3ZQ0AhujNmzd21kWeP38u63SV7hH2bVBQsaL2+nDKUiMpEVat/v+HRYmvUnsb2+DgYFmnq3SPsAEBAfqzrSP5Hj54MPSvIV5eXuLndWvWpO90KtrSPcIK8+fOG9CvnywAwFBcvXJl0IABZ8+cUalUc2bN0p/Ha6d7hBWmTZkyym2ELNJbUiKsh4dH3Zq1Vq1YGRQYNGrEyEkT0vkv9KN0j7DCiGHD9eScNJLP09PTIpe5aK2atyhRzPH8uXNyQXrThwirOWBj5AwAA3PyxAnNnr9kMcfC+QucOnlKLkhv+hBhnz9/XrSIzdu3b2WdrpISYc+eOatZuwXyWuW3zHvs2DG5IL3pQ4S9f+++o71DWFiYrKFkl/+5rNnUNa2sk9OihQsDAwPl4vSjDxFWGDxgYLrPMgMAKevI4cPae35L89ydO3QQyScyMlK+Ip3oQ4QVenbv/vfSpbJIV0mJsMePHddeu6I1rFdv5/Yd6b529SHCCn+2aq0PN60j+c6fOxdrUxdNHKK8fPFCviKd6EmEvX3rllPxEhEREbIGAOXbt3dvrN2+aHVq1Ez3x3DqSYS9/M/lCmXKpnvkE5ISYQ8dPBhr1YqmD5FcTyLs0SNH6tepKwso2amTJ2Nt5zaFCv/j8Y9cnH70JMIK4vD15IkTsgAA5du5fUesPb/Ir/pw/U1PIqxQrVLlK5cvyyL9JCXC7tm9O9banT93nlyWrvQkwqpUKgdbO78nT2QNxRJHI9rbedEiNp7Xr8tl6Up/Iqz4Rzd86FBZAIDybdm0SXvPryf5VdCfCDttytSJ4yfIIv0kJcJu37ZNe+26T5suF6Q3PYmwwoB+/ZYvWyYLKNaBffs/bufF7IrevnVLLkhv+hNhvb29SxZz1IcrSgCQItatXftxz68/+VXQnwjr6enpXK68LNJPUiLsxvUbPq7dCePG68+3l/5E2EMHD+rJHPhIjl07dmq28xIOxe7dvSd79YD+RFihWqXKl/9J/ytKAJAiVixfrtnz61V+FfQnwgplnZy87tyRRTpJSoRds2q1Zu2OchuhV2df9CfChoWG2RS2Tvcn6SOZtmzeLLbz0iVKent7yy79oFcRdtqUqfqzVwWAZFqyaLEe5ldBryLs6JGjxJ9HFukkKRH276VLxdr9a+Ag7Wcc6AP9ibBCp/Yddu3cKQso0/p168qXLiPyoqz1hl5F2DOnTzdt1FgWAKBw8+fO08P8KuhVhN23d2+Hdq6ySCdJibAiKfbt1VulUslab+hVhBWb2vixY2UBZTp18uTTp09loU/0KsK+DQqyLlBQD3cIAJAEhw8d0sP8KuhVhPXz8yvu4CCLdJKUCOt5/bp+zgSpVxH29KlTzZs0lQWQovQqwgoVy1dI90FRAGDY9CrCCiLCpu/kS0mJsHpLryJsUGCQTaHC+jbWAoZB3yJs3169N23YKAsAQCrQtwjboZ3r3j17ZJEeiLCpyLlsOb26jR0GQ98i7N9Ll44YNlwWAIBUoG8RdtaMGdOmTJFFeiDCpqIunTql7wEKDJW+RdjDhw61b9tOFgCAVKBvEXbzxk39+/SVRXogwqaiUW4jeMABUoO+RVjP69dr16ghCwBAKtC3CHv61KlWzVvIIj0QYVOR2NomTdCjrQ0GQ98i7Av/F4726XxrKgAYNn2LsPfu3qvs7CyL9ECETUVbNm3q17uPLICUo28RVq1W57fMq58TlQCAYdC3CPv27dsiBQvJIj0QYVPRqZMn0/ccOwyVvkVYwal4Cf2cQxcADIO+RVihUL7879+/l0WaI8Kmops3btaqXl0WQMrRwwhbsXyFhw8eyAIAkNL0MMIWLWKTjs/SJ8KmIq87d6pVqiwLIOXoYYQVm7qXl5csAAApTQ8jbHEHhxf+L2SR5oiwqcjHx4eBBEgNehhhT5869TYoSBYAgJS2euWqhfPny0I/iN1+ZGSkLNKcQUXYQwcPbt2yRRaA4YqIiEjfx/oBAJC+DCrCAgAAwBgQYQEAAKAwRFgAAAAoDBEWAAAACkOEBQAAgMIQYQEAAKAwRFgAAAAoDBEWAAAACkOEBQAAgMIQYQEAAKAwRFgAAAAoDBEWAAAACkOEBQAAgMIQYQEAAKAwRFgAAAAoDBEWAAAACkOEBQAAgMIQYQEAAKAwRFgAAAAoDBEWAAAACkOEBQAAgMIQYQEAAKAwRFgAAAAoDBEWAAAACkOEBQAAgMIQYQElCQsLO3/u3KGDB48fPeb98KHsBQDAyBBhAWV47Pt44vgJDrZ2FrnMP7ZWzVsc2H8gMjJSvggAAONAhAUU4IbnDTvrItrhVbuJaEuKBQBDEhYWtmvHzpbNmpVwKFa0iE2ZUk4jhg2/d/eeXAyDibAP7t8fN2ZM6ZKlrAsUtClUuHqVqsv//vvt27dyMaBkN2/cjCe/apr7tOny1QAAhTt39lwxu6Kx9vOa1rtnzw8fPsjXGTfFR9iIiIj+ffvFWsGaJuLsgf0H5OsAxWrSsFGsbVtn8/b2lm8AACiWyK8FrfLF2sNrtx5du4nwI19txJQdYcUq7Nmte6xVq93yWVgePXJEvhpQIK87d2Jt1XG1SRMmyvcAAJTJ39+/UL78sXbvn7aZ7u7yDUZM2RF2xnT3WCv101Ygr5Wfn598A6A048aMibVJx9Uc7R3kewAAyjRn1qxY+3adrYRDMYYTKDjChoeHi+/sWCtVZ5s6ecp7QJk6te8Qa3uOpwUFBcm3AQCU5t27d6WKl4i1Y4+rMVRSwRF2z+7dsVYnjUaj0Wg0mjG0CePGyzxkrBQcYceOHh1rddJoNBqNRqMZQxsxbLjMQ8ZKwRF22JAhsVZnPO38+fN+gAJt3bI11sYcV+vcsZN8DwBAgTw8PGLt2ONpE8aOk3nIWBnLWdinT5/KtwGKolarncuVj7U962z/ePwj3wMYnLCwsNOnT7u7u7do0cLR0dHa2trGxqZUqVKdOnVavHjxtWvXeLoHDEBEREQpx+Kx9u1xtT27d8u3GSsFR9h9e/fGWp1xtdIlS6lUKvk2QGkOHTxolcci1lYdq3Xr3IWvcBikx48fDxky5H//+5+Tk1OfPn3Wrl175coVLy+vO3fuXLhwYeHChe3bty9YsGD+/PnnzJnDE22gdMxIkHAKjrDiYMUpYTfuzZszV74HKe3Vq1e3bt26evWq+O/Lly9lL1Lagf0H8llYxtqwP7YunTqxL4PhCQ4O7tGjx08//dSvX78HDx7IXl3E8dvZs2ebN2/+448/zp07l8M5KNerly9tChWOtZP/tBFsBAVHWGHJosWxVuqnTWwKL1+8kG9AShCxdcaMGY0aNTI3NxffLra2tsWLF7ezs8uRI8cff/zRsGFDd3d34myK8/DwEFE1b+482pt3OafSixYsIL/C8Jw5cyZv3rx//vnnmzdvZFcCeHl5lSxZsnLlyr6+vrILUJprV6/aWltr7+pjtaGD/1Kr1fLVRkzZEVYcao8fOzbWqtVuIr9evXJFvhrJduPGDVdX12zZson/bt682cfHR/tsh/hZfG1s2bKlQ4cO4jVt27a9fv26XIYU4u/vv3njpuXLlq1euerkiRPsxWCQNm3aJA6Jt2/fLuv4qAN9b928+fBFmKxVKtXEiRN/+eUXsb+SXYDS3PC8UdnZOVakES2/ZV4Re9jzayg7wgoiNs2dPadIwUKxVrNolSo4k19TSnh4uJub2//+97/Jkye/fv1a9sYtICBg6tSpP//889ChQ8V7ZS8AfI44PBa7misJ23tH3JldOVsGEzP70TdiPDJ+9erVIsXevHlT1oDSiHhz8cKFfr371K9Tt0bVao0bNJw3Z+4rrnBqUXyE1QgJCdm4fkPbP/9sWK+eWM09u3c/c/q09glCJMfdu3ft7Ozq1Knz7Nkz2ZUw/v7+9erVs7GxuXPnjuwCgLidPHkyR44cly9flnX8wm9Mq5A1g4nJpxFWWLVq1a+//ir2QrIGYFgMJMIi9Xh5eeXMmTPJd0iIdy1cuFB8kdy+fVt2AYAuwcHBefLk2bJli6w/I+zahNLfRQVY3RFW6NOnT8OGDTmdARgkIizic+/evd9++23JkiWy1kXlvX5As8aNYmvae9U9+Yp//12+fLnIwZyLBRCP7t27N2/eXBafE3ppVPHvctja/Z4p7ggbEhJiZWW1fv16WQMwIERYxCksLKxw4cKzZ8+WtW7BR7pZRH2FxGRqZud2OcY3yvz58wsWLBgaGiprANDy8OHD77///tWrV7KOX8i5Yfbf/tZ01UG3omZijxNHhBWOHj2aK1cupgYHDA8RFnEaMmRIzZo1478Gp/KeW+V7U01s1WL6fY2FvjHvmBSfU6dOnUGDBskaALQMHDiwW7dusviMd6cG2WQxd9nyLPzaiM9EWLHnsbW13blzp6wBGAoiLHS7cuXKjz/+6OfnJ2vdQo73svqqSJ89t+7E5PUoQMcsBM+ePfvpp58uXbokawCI9v79++zZs9+6dUvW8Qo82qdQlrwddr9S/xvx2QgrLF68uEqVKrIAYCiIsNCtRYsW48ePl0Uc1I8WVP/hf43XPk/4DHWTJ09u0qSJLAAg2sGDB+3t7WURv4D9XfNlyd/jUPQDDxIUYYODgzNnzpzwZ8+q1WruAAP0HxEWOjx//vzbb7998ZmnmoWe7Jvvy0KDzv03o3hCvHr1Snzy06dPZQ0A//4rDpi7du0qi3ioX+xqb5nFuv8JGUcTFGEFW1vbEydOyCIBJkyY4OzsPHfu3IRMgw0gXRBhoYPYfbdu3VoWcVD7Lq71QwYTU1OzrOb2NVzdlh33CZGL4teuXbsxY8bIAgD+/bdBgwbLli2TRZzUz7e2yf2d/dCzwbIjwRG2ffv206ZNk0XC2Nvbiw8WChQo4ObmdunSJU7NAnqFCAsdqlatunHjRlnoFnZmQEGzGPdxmX75W/meK68FyhfEaevWrRUrVpRFAjx79szT01MWAAxR/vz5L168KIs4qP3Wt/j9+xKjL2lNa5LQCDtjxgxXV1dZJFiXLl2++uqrqI83M/s2moja69at49QsoA+IsIgtMjIye/bsDx8+lLVOYRenN3N2ss//+w9fZtAOshm+K9Jhw4N4Hyjr6+v7/fffJ+p8Ru/evcVn16lTZ9u2be/evZO9AAzFH3/88Zmnn6gfrWqUM2vZSddj7F4SGmGXLl1aokSJuXPnzpkzZ/bs2bNmzZoZTURbd3f36dOnT5s2berUqVOiTZ48edKkSRMnTpwwYYKjo2PGjBmj925S5syZTU2j9nr169fn1CyQjoiwiM3b2ztbtmwJ3i+HPru6Z8HgRjbZ/zsna/pl/k4747nDS3xyjhw57t37/wcfJETFihXFZ3/zzTfi+8Pe3l58wdy4cUMuA5C2li9fbp6ivv766zvxPfpE9XBp3Z+zV5x5K1ZOTUSELViwYNeuXbt169a9e/ce0Xr27NmrVy9xhNynT5++ffv2i9a/f/8BAwYMHDhw0KBBgwcPtrOziyvCNmrU6PLly0RYIL0QYRHbyZMnixYtKosEU/mfmt7I6itNjM2Ys+Um/3imKXB0dDx27JgsEqxjx47iey76F5h88cUXIs5mz57dxcVl+/btnJoF0lJQUNDDFGVhYeHh4SE//ROqe/Or/5S94rQrr9/E8ursEJvoCFt02IWohYGBITqT7KxZs9q1ayeLBOvcufPHgQRZsmT57rvvGjduvGHDhoCAAPkKAOmHCGsIrl69ejrluLu7Ozg4yI9OFNWT7R0LfhmVYk2/cBwbzzkREZELFChQMlqJEiWKFy8uQm2xYsXE7xXs7e3FC+zs7GxtbW1sbIpEs7a2Lly4cObMmaMCrC41atS4cuWK/AUAFKVOnTorV66URWwRl4ZaR+XUBDD9psEaXfeVijA6adIkWSSM2AtpPlPseUaOHPnPP/9wwhXQK0RYQ9C2bdtSKadQoUJWVlbyoxMr1GNksa9FiDU1KznRK84nOoq0OnPmzDNnzpw9e/bcuXPnz5+/cOHCxWiXLl3y8PAQ3xaXL18WkVSkc+HatWvXr18X2fqLL77QfKkIIs5+9dVXP/zwQ4MGDWbPnh0c/PEmZQAKM3r06F69eskitvALgwt9+hRrneKKsOLY+MiRI7JIgIkTJ1apUmXBggWccAX0FhEWsYngmCdPHlkk3tud7XJmNDHJ8L+2u7TuG45JROR4LhrqtHXrVs1X1HfffSfCq6Oj49SpUz9z/wcAhdizZ0/JkiVl8Ym3Dy4eP6bTkeWuVlHpNlP+9iuPiPr4yRufjmEKCwsTR79v3kQ/DCEBIqPJAoC+IsIitsTu7mML3dX25wwmplmbb9IdYd++fSsy6Pv372WdANu3b9fk1zZt2uzcuZMTroCBeffuXdasWR/GPxGKDgm6nWvNmjVlypSRBQBDQYSFDom96BaD6s74EmYmmSx7ndQ9t9aJEydsbW1lkQBPnz6dPXv2zZs3ZQ3AEPXo0WPAgAGySKgERdiSJUuuX79eFgAMBREWOnTt2nXkyJGySCzV7XGOZhmyNV0fx0MOxo4d26FDB1kAQLTbt29nz5797Vv56NiE+XyEvXjx4s8//xweHu9s1QAUiAgLHc6dO/f7779HRMQ9p0A8Xq9p+EOm31136/wiUqlU5ubmp06dkjUA/MfFxaVz586ySJDPRNgPHz7Y2touXLhQ1gAMCBEWOkRGRtrb22/btk3WiRDuOdHp2+zV5t/XPR3Bzp07bWxsuFUCwKcCAgJy5sx56NAhWX/eZyLsyJEjK1euzA4HMEhEWOi2fPlyR0dHlUpnElV5bxvarFb9TlMP+8b41ojw3dHNNmueFmt9dL9NpSpZsuSSJUtkDQAx7dmzR6TYxD69TyfxUT/99NOjR49kDcCwEGGhm1qtLlOmzJQpU2StTe0zvVz0IwZMM/1g3Wj40l2nLl+9cGjNxPZOOX+067ThflyDzqZPny4ibByxGACiLFy48Pfff79//76sk2Tv3r0iv54/f17WAAwOERZxunfvXrZs2XRNBaD23eSa/xvNw2Q1TDN883uJFmN23Y9zLtg7d+6IT/Py8pI1AMRhwYIFIsWePn1a1okRGRm5bNkykV/PnTsnuwAYIiIs4iO+Cf74448HDx7IWkuw96kN8yaOHDZ81OTZyzYevf0mvnOrPj4+5ubmixcvljUAxGvnzp2//vrrwIEDQ0PjPDD+1NOnT2vVqlW0aNEbN27ILgAGigiLz5g7d26uXLkSP+X4/3v06FHu3LlnzZolawBIgJcvXzZp0kQcRU+aNOnVq1eyNw53797t1atX9uzZR4wY8eHDB9kLwHARYfF5IsX+/PPPmzdvlnVibNu27ZdffiG/Akiaa9eutW/fPlu2bPXr158wYcLhw4efPn0aGBgYEBDg7e0t9jBDhgwpX7682M+4ubn5+fnJtwEwdERYJMiFCxfy58/fokWLx48fy67PEd8lrVu3trKyYkQagGR6/fr11q1b//rrr0qVKuXKlStHjhziuNrS0lLk2nHjxh08eJCHFwDGhgiLhHr//v2QIUN+/PHHxo0bnzx5Mq6JBdRq9alTp5o2bSpeOWjQoJCQELkAAAAghRBhkTjv3r2bP3++nZ3d999/X65cuX79+k2fPn3u3Lnu7u4DBgwoX7581qxZbW1t582bJ14p3wMAAJCiiLBIIpFQT5w4MW3atEGDBvXu3Vv8d+rUqcePH0/kI84BAAASjQgLAAAAhSHCAgAAQGGIsAAAAFAYIiwAAAAUhggLAAAAhSHCAgAAQGGIsAAAAFAYIiwAAAAUhggLAAAAhSHCAgAAQGGIsAAAAFAYIiwAAAAUhggLAAAAhSHCAgAAQGGIsAAAAFAYIiwAAAAUhggLAAAAhSHCAgAAQGGIsAAAAFAYIiwAAAAUhggLAAAAhSHCAgAAQGGIsAAAAFAYIiwAAAAUhggLAAAAhSHCAgAAQGGIsAAAAFAYIiwAAAAUhggLAAAAhSHCAgAAQGGIsAAAAFAYIiwAAAAUhggLAAAAhSHCAgAAQGGIsAAAAFAYIiwAAAAU5d9//w/8SsoXIt3MoQAAAABJRU5ErkJggg==" + } + }, + "cell_type": "markdown", + "id": "1ebd6b2b-7fac-4d12-bf88-7288c1d41ad1", + "metadata": {}, + "source": [ + "Consider a beam with the length 9.0 m. The beam is simply supported and loaded\n", + "by a point load $P=10000 N$ applied at a point 3.0 m from the left support. The\n", + "corresponding computational model has six degrees of freedom and consists of two\n", + "beam elements with four degrees of freedom. The beam has Young’s modulus $E=210 GPa$ and moment of inertia $I=2510\\times 10^{−8} m^4$.\n", + "
\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "748d8d1c-c8d9-4b8d-9465-b250c5bd5677", + "metadata": {}, + "source": [ + "Install CALFEM (if required)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "fa33f6e7-8616-43c1-ab6d-c033d33d5a4a", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'list' object has no attribute 'prepend'", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[2], line 5\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mimportlib\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mutil\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m find_spec\n\u001b[0;32m 3\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01msys\u001b[39;00m\n\u001b[1;32m----> 5\u001b[0m sys\u001b[38;5;241m.\u001b[39mpath\u001b[38;5;241m.\u001b[39mprepend(\u001b[38;5;124mr\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mC:\u001b[39m\u001b[38;5;124m\\\u001b[39m\u001b[38;5;124mUsers\u001b[39m\u001b[38;5;124m\\\u001b[39m\u001b[38;5;124mJonas Lindemann\u001b[39m\u001b[38;5;124m\\\u001b[39m\u001b[38;5;124mDevelopment\u001b[39m\u001b[38;5;124m\\\u001b[39m\u001b[38;5;124mcalfem-python\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 7\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m find_spec(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcalfem.core\u001b[39m\u001b[38;5;124m\"\u001b[39m) \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m 8\u001b[0m get_ipython()\u001b[38;5;241m.\u001b[39msystem(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mpip install calfem-python\u001b[39m\u001b[38;5;124m'\u001b[39m)\n", + "\u001b[1;31mAttributeError\u001b[0m: 'list' object has no attribute 'prepend'" + ] + } + ], + "source": [ + "from importlib.util import find_spec\n", + "\n", + "import sys\n", + "\n", + "sys.path.prepend(r\"C:\\Users\\Jonas Lindemann\\Development\\calfem-python\")\n", + "\n", + "if find_spec(\"calfem.core\") is None:\n", + " !pip install calfem-python\n", + "else:\n", + " print(\"CALFEM already installed.\")" + ] + }, + { + "cell_type": "markdown", + "id": "a8fcd0e8-c7d6-4de4-af95-108d20153c7d", + "metadata": {}, + "source": [ + "Import modules" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "3d33663a-d8e9-46cf-b797-4f280930aac9", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import calfem.core as cfc\n", + "import calfem.utils as cfu\n", + "import calfem.vis_mpl as cfv" + ] + }, + { + "cell_type": "markdown", + "id": "c4182177-e7f7-423a-a032-ce1259a6ee84", + "metadata": {}, + "source": [ + "Define topology" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "ba1a0696-f45f-4798-ba51-f08be6dad7df", + "metadata": {}, + "outputs": [], + "source": [ + "edof = np.array([\n", + " [1, 2, 3, 4], \n", + " [3, 4, 5, 6]\n", + "])" + ] + }, + { + "cell_type": "markdown", + "id": "e7984716-86ed-4d05-a4f9-e41bdeb29dd8", + "metadata": {}, + "source": [ + "Setup stiffness matrix K and load vector f" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "f0a9449b-b9f7-42bb-b9c4-dabaa01c5875", + "metadata": {}, + "outputs": [], + "source": [ + "K = np.array(np.zeros((6, 6)))\n", + "f = np.array(np.zeros((6, 1)))\n", + "f[2] = -10e3" + ] + }, + { + "cell_type": "markdown", + "id": "30069d9d-a320-4019-b48a-2ef581e719e4", + "metadata": {}, + "source": [ + "Setup element stiffness and element load matrices" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "9a39be9c-bb81-4caa-9de9-5fafdcb29013", + "metadata": {}, + "outputs": [], + "source": [ + "E = 210e9\n", + "I = 2510e-8\n", + "\n", + "ep = np.array([E, I])\n", + "ex1 = np.array([0, 3])\n", + "ex2 = np.array([3, 9])\n", + "eq1 = np.array([0])\n", + "eq2 = np.array([0])\n", + "\n", + "Ke1 = cfc.beam1e(ex1, ep)\n", + "Ke2 = cfc.beam1e(ex2, ep)" + ] + }, + { + "cell_type": "markdown", + "id": "0bd2bd78-370c-494e-87ea-528620b992ff", + "metadata": {}, + "source": [ + "Assemble Ke into K" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "894a19bd-0eb9-42e8-bfff-5f6b92fc8c9a", + "metadata": {}, + "outputs": [], + "source": [ + "K = cfc.assem(edof[0, :], K, Ke1)\n", + "K = cfc.assem(edof[1, :], K, Ke2)" + ] + }, + { + "cell_type": "markdown", + "id": "f0da7c93-bafe-42d9-87ca-c26959e98f63", + "metadata": {}, + "source": [ + "Solve the system of equations and compute reactions" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "cc77fe68-7abe-48e2-a5b3-ef891e781d4b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
a
0.0000e+00
-9.4859e-03
-2.2766e-02
-3.7943e-03
0.0000e+00
7.5887e-03
" + ], + "text/plain": [ + "'\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
a
0.0000e+00
-9.4859e-03
-2.2766e-02
-3.7943e-03
0.0000e+00
7.5887e-03
'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
r
6.6667e+03
-3.6380e-12
7.2760e-12
-1.0914e-11
3.3333e+03
0.0000e+00
" + ], + "text/plain": [ + "'\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
r
6.6667e+03
-3.6380e-12
7.2760e-12
-1.0914e-11
3.3333e+03
0.0000e+00
'" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "bc = np.array([1, 5])\n", + "a, r = cfc.solveq(K, f, bc)\n", + "\n", + "cfu.disp_array(a, [\"a\"])\n", + "cfu.disp_array(r, [\"r\"])" + ] + }, + { + "cell_type": "markdown", + "id": "607f92c9-6f7e-4f72-b403-26017aacc5c7", + "metadata": {}, + "source": [ + "Section forces" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "cee82b10-4feb-49d3-ac3c-b4c45a29aa12", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "

es1

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
V1 M1
-6.6667e+039.1437e-12
-6.6667e+036.6667e+03
-6.6667e+031.3333e+04
-6.6667e+032.0000e+04
" + ], + "text/plain": [ + "'\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
V1 M1
-6.6667e+039.1437e-12
-6.6667e+036.6667e+03
-6.6667e+031.3333e+04
-6.6667e+032.0000e+04
'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "

ed1

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
v1
0.0000e+00
-9.2751e-03
-1.7285e-02
-2.2766e-02
" + ], + "text/plain": [ + "'\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
v1
0.0000e+00
-9.2751e-03
-1.7285e-02
-2.2766e-02
'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "

ec1

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
x1
0.0000e+00
1.0000e+00
2.0000e+00
3.0000e+00
" + ], + "text/plain": [ + "'\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
x1
0.0000e+00
1.0000e+00
2.0000e+00
3.0000e+00
'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "

es2

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
V2 M2
3.3333e+032.0000e+04
3.3333e+031.6667e+04
3.3333e+031.3333e+04
3.3333e+031.0000e+04
3.3333e+036.6667e+03
3.3333e+033.3333e+03
3.3333e+034.5719e-12
" + ], + "text/plain": [ + "'\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
V2 M2
3.3333e+032.0000e+04
3.3333e+031.6667e+04
3.3333e+031.3333e+04
3.3333e+031.0000e+04
3.3333e+036.6667e+03
3.3333e+033.3333e+03
3.3333e+034.5719e-12
'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "

ed2

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
v2
-2.2766e-02
-2.4769e-02
-2.3609e-02
-1.9920e-02
-1.4334e-02
-7.4833e-03
6.9389e-18
" + ], + "text/plain": [ + "'\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
v2
-2.2766e-02
-2.4769e-02
-2.3609e-02
-1.9920e-02
-1.4334e-02
-7.4833e-03
6.9389e-18
'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "

ec2

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
x2
0.0000e+00
1.0000e+00
2.0000e+00
3.0000e+00
4.0000e+00
5.0000e+00
6.0000e+00
" + ], + "text/plain": [ + "'\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
x2
0.0000e+00
1.0000e+00
2.0000e+00
3.0000e+00
4.0000e+00
5.0000e+00
6.0000e+00
'" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ed = cfc.extract_ed(edof, a)\n", + "\n", + "es1, ed1, ec1 = cfc.beam1s(ex1, ep, ed[0, :], eq1, nep=4)\n", + "es2, ed2, ec2 = cfc.beam1s(ex2, ep, ed[1, :], eq2, nep=7)\n", + "\n", + "cfu.disp_h2(\"es1\")\n", + "cfu.disp_array(es1, [\"V1\", \"M1\"])\n", + "cfu.disp_h2(\"ed1\")\n", + "cfu.disp_array(ed1, [\"v1\"])\n", + "cfu.disp_h2(\"ec1\")\n", + "cfu.disp_array(ec1, [\"x1\"])\n", + "cfu.disp_h2(\"es2\")\n", + "cfu.disp_array(es2, [\"V2\", \"M2\"])\n", + "cfu.disp_h2(\"ed2\")\n", + "cfu.disp_array(ed2, [\"v2\"])\n", + "cfu.disp_h2(\"ec2\")\n", + "cfu.disp_array(ec2, [\"x2\"])" + ] + }, + { + "cell_type": "markdown", + "id": "18d2b635-7d45-42e0-8f45-cdfd7e56352c", + "metadata": {}, + "source": [ + "Draw deformed frame" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "04d68478-5ac0-45cf-8903-5ac389f8faa8", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "cfv.figure(1)\n", + "plt.plot([0, 9], [0, 0], color=(0.8, 0.8, 0.8))\n", + "plt.plot(\n", + " np.concatenate(([0], ec1[:, 0], 3 + ec2[:, 0], [9]), 0),\n", + " np.concatenate(([0], ed1[:, 0], ed2[:, 0], [0]), 0),\n", + " color=(0.0, 0.0, 0.0),\n", + ")\n", + "cfv.title(\"displacements\")" + ] + }, + { + "cell_type": "markdown", + "id": "e6fbdfaa-d28b-49da-9d8d-07b016dcec6e", + "metadata": {}, + "source": [ + "Draw normal force diagram" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "9e420575-f743-4a5d-80d1-0591e73d1f45", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "cfv.figure(2)\n", + "plt.plot([0, 9], [0, 0], color=(0.8, 0.8, 0.8))\n", + "plt.plot(\n", + " np.concatenate(([0], ec1[:, 0], 3 + ec2[:, 0], [9]), 0),\n", + " -np.concatenate(([0], es1[:, 0], es2[:, 0], [0]), 0) / 1000,\n", + " color=(0.0, 0.0, 0.0),\n", + ")\n", + "cfv.title(\"shear force\")" + ] + }, + { + "cell_type": "markdown", + "id": "57508ff2-21f5-48cd-9bb5-d0ad268b9e13", + "metadata": {}, + "source": [ + "Draw shear force diagram" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "4734b313-1233-4f7f-bd97-cbbe411ced44", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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TaJo0aIeOY5ffmdxcHCwye/3EZw4cYLatWvTrVs3Ro0aVcDJlFKqYGRkZNCpUydmzZrFwoULad68udv7FJHlxpjgS58PuCuLy5UrR/fu3YmLi+P48eO24yilVLY+/PBDfvzxRz7//PMCKYHcBFwRQObpoZMnTxIfH287ilJK/c3cuXN54403CA8P5x//8OjZciAATw1B5kexmjRpQpUqVfj1118LMJlSSrln9+7dNGvWjGrVqrFs2TLKlStXYPvWU0NZiAhOp5NFixaxfv1623GUUgqA8+fP061bN06fPs3kyZMLtARyE5BFANC7d2+KFSvGmDFjbEdRSikABg0axKJFixg1ahQ33HCD144bsEVQs2ZNOnbsyLhx4zxypZ5SSl2JhIQEvvjiC5555hm6devm1WMHbBFA5qDxgQMH+PHHH21HUUoFsI0bN+JwOGjRogWffPKJ148f0EXQvn17ateuzejRo21HUUoFqJMnTxISEkKpUqWIj4+nRIkSXs8Q0EVQrFgxIiMjmT59Onv3emRWC6WUypExhn79+rFu3TpiYmKoV6+elRwBXQQADoeD9PR0xo8fbzuKUirADB8+nEmTJvHuu+96ZDK5vArI6wgudc8997B//342bNiASHazYiulVMH67bffaNWqFW3btuWHH36gSBHP/16u1xHkwul0smnTJhYuXGg7ilIqABw8eJDQ0FBq167NhAkTvFICudEiAEJDQylfvrwOGiulPC4jI4OIiAj27dtHYmIiVapUsR1JiwCgbNmy9OjRg/j4eI4dO2Y7jlKqEBs8eDA//fQTQ4YMITj4b2dprNAicHE6nZw6dYq4uDjbUZRShdSsWbN4++236d27N/369bMd5790sNjFGEPTpk2pUKECixcvLtB9K6XUzp07adasGbVr12bp0qWUKVPG6xl0sPgyLk5Et2TJEtatW2c7jlKqEDl37hxhYWGcO3eOpKQkKyWQGy2CLCIiIihWrJgOGiulCtTAgQNZunQpo0eP5rrrrrMd52+0CLKoUaMGnTp1Yvz48Zw7d852HKVUIRAbG8vQoUN5/vnnCQ0NtR0nW1oEl3A6naSlpTFt2jTbUZRSfm7dunU89thjtGzZko8++sh2nBxpEVzigQceoE6dOnp6SCnlluPHjxMSEkLZsmWJj4+nePHitiPlSIvgEsWKFaNv377MmDGD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Uvl37bXXcu+99zJ69Gj88boR9VdLlizhhRdeoGPHjrz00ku246hCwBtjBCHASmPM2RyWPyUiq0VktIhU9kKegORwONiyZQv/93//ZzuKckNaWhphYWHUq1eP8ePH62RyqkBc9m+RiMwRkZRsbp3zsO1NwEdATte6fw1cA9wG7AU+zWVfT4hIsogkp6WlXe7Q6hKhoaGUL19eB439WHp6Or169SItLY3ExEQqV9bfm1TBuGwRGGPaGmOaZnObktt2IlIP+A7oY4z5I4d97zfGpBtjMoCRQPNccowwxgQbY4KrV69+udjqEmXKlCE8PJyEhASOHTtmO47Kh3fffZfZs2czdOhQgoKCbMdRhYhH3leKSCXgR+AVY0yOk+KLSO0sD7uSOfisPMTpdHL69GliY2NtR1FXaMaMGbz33nv07dsXp9NpO44qZNyadE5EugJfAdWBI8AqY8yDIvI68AqwOcvqDxhjDojIt8BwY0yyiEwg87SQAbYB/Ywxl/2yXZ10Ln+MMdxyyy2ULVuWJUuW2I6j8mj79u0EBQVRr149Fi9eTJkyZWxHUn4qp0nndPbRAPP555/zwgsvkJKSwk033WQ7jrqMs2fP0qpVKzZt2sTy5cu59tprbUdSfkxnH1UAREREULx4cR009hPPP/88ycnJjB07VktAeYwWQYCpXr06nTp1YsKECZw7d852HJWLiRMn8vXXX/Piiy/StWtX23FUIaZFEICcTid//vknP/zwg+0oKgcpKSk88cQT3HPPPbz//vu246hCTosgAD3wwAPUrVtXv6fARx07doyQkBAqVKhAbGwsxYoVsx1JFXJaBAGoaNGi9O3bl59++ondu3fbjqOyMMbgdDr5448/iIuLo3bt2pffSCk3aREEqKioKDIyMhg7dqztKCqLIUOGkJiYyPvvv8+9995rO44KEPrx0QDWunVrduzYwebNm3XOGh/w66+/ct999/Hwww/z3XffISK2I6lCRj8+qv7G6XSSmprKggWXziKuvO3AgQN069aNq666irFjx2oJKK/SIghgISEhVKxYUQeNLUtPTyc8PJxDhw6RlJREpUqVbEdSAUaLIICVLl2a8PBwEhMTOXr0qO04AevNN99k3rx5/Oc//+HWW2+1HUcFIC2CAHdxIrqYmBjbUQLStGnTeP/993nssceIioqyHUcFKB0sDnDGGG69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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "cfv.figure(3)\n", + "plt.plot([0, 9], [0, 0], color=(0.8, 0.8, 0.8))\n", + "plt.plot(\n", + " np.concatenate(([0], ec1[:, 0], 3 + ec2[:, 0], [9]), 0),\n", + " -np.concatenate(([0], es1[:, 1], es2[:, 1], [0]), 0) / 1000,\n", + " color=(0.0, 0.0, 0.0),\n", + ")\n", + "cfv.title(\"bending moment\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/exs_beam1.py b/examples/exs_beam1.py index 4608101..0361cd1 100644 --- a/examples/exs_beam1.py +++ b/examples/exs_beam1.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- # # example exs_beam1 -#---------------------------------------------------------------- -# PURPOSE +# ---------------------------------------------------------------- +# PURPOSE # Analysis of a simply supported beam. -#---------------------------------------------------------------- +# ---------------------------------------------------------------- # REFERENCES # Ola Dahlblom 2015-11-13 @@ -21,15 +21,15 @@ # ----- Topology ------------------------------------------------- edof = np.array([ - [1, 2, 3, 4], - [3, 4, 5, 6] + [1, 2, 3, 4], + [3, 4, 5, 6] ]) # ----- Stiffness matrix K and load vector f --------------------- K = np.array(np.zeros((6, 6))) f = np.array(np.zeros((6, 1))) -f[2] = -10e+3 +f[2] = -10e3 # ----- Element stiffness and element load matrices ------------- @@ -55,10 +55,8 @@ bc = np.array([1, 5]) a, r = cfc.solveq(K, f, bc) -print("a = ") -print(a) -print("r = ") -print(r) +cfu.disp_array(a, ["a"]) +cfu.disp_array(r, ["r"]) # ----- Section forces ------------------------------------------- @@ -67,34 +65,50 @@ es1, ed1, ec1 = cfc.beam1s(ex1, ep, ed[0, :], eq1, nep=4) es2, ed2, ec2 = cfc.beam1s(ex2, ep, ed[1, :], eq2, nep=7) -print("es1 = ") -print(es1) -print("ed1 = ") -print(ed1) -print("es2 = ") -print(es2) -print("ed2 = ") -print(ed2) - -#----- Draw deformed beam --------------------------------------- - +cfu.disp_h2("es1") +cfu.disp_array(es1, ["V1", "M1"]) +cfu.disp_h2("ed1") +cfu.disp_array(ed1, ["v1"]) +cfu.disp_h2("ec1") +cfu.disp_array(ec1, ["x1"]) +cfu.disp_h2("es2") +cfu.disp_array(es2, ["V2", "M2"]) +cfu.disp_h2("ed2") +cfu.disp_array(ed2, ["v2"]) +cfu.disp_h2("ec2") +cfu.disp_array(ec2, ["x2"]) + +# ----- Draw deformed beam --------------------------------------- + cfv.figure(1) -plt.plot([0, 9],[0, 0], color=(0.8, 0.8, 0.8)) -plt.plot(np.concatenate(([0],ec1[:,0], 3+ec2[:,0],[9]), 0),np.concatenate(([0],ed1[:,0], ed2[:,0],[0]), 0), color=(0.0, 0.0, 0.0)) -cfv.title('displacements') - -#----- Draw shear force diagram---------------------------------- +plt.plot([0, 9], [0, 0], color=(0.8, 0.8, 0.8)) +plt.plot( + np.concatenate(([0], ec1[:, 0], 3 + ec2[:, 0], [9]), 0), + np.concatenate(([0], ed1[:, 0], ed2[:, 0], [0]), 0), + color=(0.0, 0.0, 0.0), +) +cfv.title("displacements") + +# ----- Draw shear force diagram---------------------------------- cfv.figure(2) -plt.plot([0, 9],[0, 0], color=(0.8, 0.8, 0.8)) -plt.plot(np.concatenate(([0],ec1[:,0], 3+ec2[:,0],[9]), 0),-np.concatenate(([0],es1[:,0], es2[:,0],[0]), 0)/1000, color=(0.0, 0.0, 0.0)) -cfv.title('shear force') +plt.plot([0, 9], [0, 0], color=(0.8, 0.8, 0.8)) +plt.plot( + np.concatenate(([0], ec1[:, 0], 3 + ec2[:, 0], [9]), 0), + -np.concatenate(([0], es1[:, 0], es2[:, 0], [0]), 0) / 1000, + color=(0.0, 0.0, 0.0), +) +cfv.title("shear force") -#----- Draw moment diagram---------------------------------- +# ----- Draw moment diagram---------------------------------- cfv.figure(3) -plt.plot([0, 9],[0, 0], color=(0.8, 0.8, 0.8)) -plt.plot(np.concatenate(([0],ec1[:,0], 3+ec2[:,0],[9]), 0),-np.concatenate(([0],es1[:,1], es2[:,1],[0]), 0)/1000, color=(0.0, 0.0, 0.0)) -cfv.title('bending moment') - -cfv.showAndWait() +plt.plot([0, 9], [0, 0], color=(0.8, 0.8, 0.8)) +plt.plot( + np.concatenate(([0], ec1[:, 0], 3 + ec2[:, 0], [9]), 0), + -np.concatenate(([0], es1[:, 1], es2[:, 1], [0]), 0) / 1000, + color=(0.0, 0.0, 0.0), +) +cfv.title("bending moment") + +cfv.show_and_wait() diff --git a/examples/exs_beam2.ipynb b/examples/exs_beam2.ipynb new file mode 100644 index 0000000..a105e86 --- /dev/null +++ b/examples/exs_beam2.ipynb @@ -0,0 +1,800 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Analysis of a simply supported beam" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "Install CALFEM (if required)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CALFEM already installed.\n" + ] + } + ], + "source": [ + "from importlib.util import find_spec\n", + "\n", + "if find_spec(\"calfem.core\") is None:\n", + " !pip install calfem-python\n", + "else:\n", + " print(\"CALFEM already installed.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Import modules" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "import numpy as np\n", + "sys.path.insert(0, r'D:\\Users\\Jonas\\Development\\calfem-python')\n", + "import calfem.core as cfc\n", + "import calfem.utils as cfu\n", + "import calfem.vis_mpl as cfv" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Define topology" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "edof = np.array([\n", + " [4, 5, 6, 1, 2, 3], \n", + " [7, 8, 9, 10, 11, 12], \n", + " [4, 5, 6, 7, 8, 9]\n", + "])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Setup stiffness matrix K and load vector f" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "K = np.array(np.zeros((12, 12)))\n", + "f = np.array(np.zeros((12, 1)))\n", + "f[3] = 2.0e3" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Setup element stiffness and element load matrices" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "E = 200.0e9\n", + "A1 = 2.0e-3\n", + "A2 = 6.0e-3\n", + "I1 = 1.6e-5\n", + "I2 = 5.4e-5\n", + "\n", + "ep1 = np.array([E, A1, I1])\n", + "ep3 = np.array([E, A2, I2])\n", + "ex1 = np.array([0, 0])\n", + "ex2 = np.array([6, 6])\n", + "ex3 = np.array([0, 6])\n", + "ey1 = np.array([4, 0])\n", + "ey2 = np.array([4, 0])\n", + "ey3 = np.array([4, 4])\n", + "eq1 = np.array([0, 0])\n", + "eq2 = np.array([0, 0])\n", + "eq3 = np.array([0, -10e3])\n", + "\n", + "Ke1 = cfc.beam2e(ex1, ey1, ep1)\n", + "Ke2 = cfc.beam2e(ex2, ey2, ep1)\n", + "Ke3, fe3 = cfc.beam2e(ex3, ey3, ep3, eq3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Assemble Ke into K" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "K = cfc.assem(edof[0, :], K, Ke1)\n", + "K = cfc.assem(edof[1, :], K, Ke2)\n", + "K, f = cfc.assem(edof[2, :], K, Ke3, f, fe3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Solve the system of equations and compute reactions" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
a (m)
0 0.0000e+00
1 0.0000e+00
2 0.0000e+00
3 7.5357e-03
4-2.8741e-04
5-5.3735e-03
6 7.5161e-03
7-3.1259e-04
8 4.6656e-03
9 0.0000e+00
10 0.0000e+00
11-5.1513e-03
" + ], + "text/plain": [ + "'\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
a (m)
0 0.0000e+00
1 0.0000e+00
2 0.0000e+00
3 7.5357e-03
4-2.8741e-04
5-5.3735e-03
6 7.5161e-03
7-3.1259e-04
8 4.6656e-03
9 0.0000e+00
10 0.0000e+00
11-5.1513e-03
'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
r (N)
0 1.9268e+03
1 2.8741e+04
2 4.4527e+02
3 0.0000e+00
4 0.0000e+00
5 0.0000e+00
6-2.3283e-10
7 3.6380e-12
8 3.6380e-12
9-3.9268e+03
10 3.1259e+04
11 0.0000e+00
" + ], + "text/plain": [ + "'\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
r (N)
0 1.9268e+03
1 2.8741e+04
2 4.4527e+02
3 0.0000e+00
4 0.0000e+00
5 0.0000e+00
6-2.3283e-10
7 3.6380e-12
8 3.6380e-12
9-3.9268e+03
10 3.1259e+04
11 0.0000e+00
'" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "bc = np.array([1, 2, 3, 10, 11])\n", + "a, r = cfc.solveq(K, f, bc)\n", + "\n", + "cfu.disp_array(a, [\"a (m)\"])\n", + "cfu.disp_array(r, [\"r (N)\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Section forces" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "

es1

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
N Vy Mz
0-2.8741e+041.9268e+038.1523e+03
1-2.8741e+041.9268e+037.7670e+03
2-2.8741e+041.9268e+037.3816e+03
3-2.8741e+041.9268e+036.9963e+03
4-2.8741e+041.9268e+036.6109e+03
5-2.8741e+041.9268e+036.2256e+03
6-2.8741e+041.9268e+035.8402e+03
7-2.8741e+041.9268e+035.4548e+03
8-2.8741e+041.9268e+035.0695e+03
9-2.8741e+041.9268e+034.6841e+03
10-2.8741e+041.9268e+034.2988e+03
11-2.8741e+041.9268e+033.9134e+03
12-2.8741e+041.9268e+033.5281e+03
13-2.8741e+041.9268e+033.1427e+03
14-2.8741e+041.9268e+032.7574e+03
15-2.8741e+041.9268e+032.3720e+03
16-2.8741e+041.9268e+031.9867e+03
17-2.8741e+041.9268e+031.6013e+03
18-2.8741e+041.9268e+031.2160e+03
19-2.8741e+041.9268e+038.3062e+02
20-2.8741e+041.9268e+034.4527e+02
" + ], + "text/plain": [ + "'\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
N Vy Mz
0-2.8741e+041.9268e+038.1523e+03
1-2.8741e+041.9268e+037.7670e+03
2-2.8741e+041.9268e+037.3816e+03
3-2.8741e+041.9268e+036.9963e+03
4-2.8741e+041.9268e+036.6109e+03
5-2.8741e+041.9268e+036.2256e+03
6-2.8741e+041.9268e+035.8402e+03
7-2.8741e+041.9268e+035.4548e+03
8-2.8741e+041.9268e+035.0695e+03
9-2.8741e+041.9268e+034.6841e+03
10-2.8741e+041.9268e+034.2988e+03
11-2.8741e+041.9268e+033.9134e+03
12-2.8741e+041.9268e+033.5281e+03
13-2.8741e+041.9268e+033.1427e+03
14-2.8741e+041.9268e+032.7574e+03
15-2.8741e+041.9268e+032.3720e+03
16-2.8741e+041.9268e+031.9867e+03
17-2.8741e+041.9268e+031.6013e+03
18-2.8741e+041.9268e+031.2160e+03
19-2.8741e+041.9268e+038.3062e+02
20-2.8741e+041.9268e+034.4527e+02
'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "

edi1

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
u1 v1
02.8741e-047.5357e-03
12.7304e-046.5112e-03
22.5867e-045.5837e-03
32.4430e-044.7485e-03
42.2993e-044.0008e-03
52.1556e-043.3357e-03
62.0119e-042.7484e-03
71.8682e-042.2341e-03
81.7245e-041.7880e-03
91.5807e-041.4053e-03
101.4370e-041.0811e-03
111.2933e-048.1067e-04
121.1496e-045.8915e-04
131.0059e-044.1173e-04
148.6223e-052.7359e-04
157.1852e-051.6993e-04
165.7482e-059.5907e-05
174.3111e-054.6722e-05
182.8741e-051.7554e-05
191.4370e-053.5858e-06
200.0000e+001.7347e-18
" + ], + "text/plain": [ + "'\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
u1 v1
02.8741e-047.5357e-03
12.7304e-046.5112e-03
22.5867e-045.5837e-03
32.4430e-044.7485e-03
42.2993e-044.0008e-03
52.1556e-043.3357e-03
62.0119e-042.7484e-03
71.8682e-042.2341e-03
81.7245e-041.7880e-03
91.5807e-041.4053e-03
101.4370e-041.0811e-03
111.2933e-048.1067e-04
121.1496e-045.8915e-04
131.0059e-044.1173e-04
148.6223e-052.7359e-04
157.1852e-051.6993e-04
165.7482e-059.5907e-05
174.3111e-054.6722e-05
182.8741e-051.7554e-05
191.4370e-053.5858e-06
200.0000e+001.7347e-18
'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "

es2

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
N Vy Mz
0-3.1259e+04-3.9268e+03-1.5707e+04
1-3.1259e+04-3.9268e+03-1.4922e+04
2-3.1259e+04-3.9268e+03-1.4136e+04
3-3.1259e+04-3.9268e+03-1.3351e+04
4-3.1259e+04-3.9268e+03-1.2566e+04
5-3.1259e+04-3.9268e+03-1.1780e+04
6-3.1259e+04-3.9268e+03-1.0995e+04
7-3.1259e+04-3.9268e+03-1.0210e+04
8-3.1259e+04-3.9268e+03-9.4242e+03
9-3.1259e+04-3.9268e+03-8.6389e+03
10-3.1259e+04-3.9268e+03-7.8535e+03
11-3.1259e+04-3.9268e+03-7.0682e+03
12-3.1259e+04-3.9268e+03-6.2828e+03
13-3.1259e+04-3.9268e+03-5.4975e+03
14-3.1259e+04-3.9268e+03-4.7121e+03
15-3.1259e+04-3.9268e+03-3.9268e+03
16-3.1259e+04-3.9268e+03-3.1414e+03
17-3.1259e+04-3.9268e+03-2.3561e+03
18-3.1259e+04-3.9268e+03-1.5707e+03
19-3.1259e+04-3.9268e+03-7.8535e+02
20-3.1259e+04-3.9268e+03 5.5511e-12
" + ], + "text/plain": [ + "'\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
N Vy Mz
0-3.1259e+04-3.9268e+03-1.5707e+04
1-3.1259e+04-3.9268e+03-1.4922e+04
2-3.1259e+04-3.9268e+03-1.4136e+04
3-3.1259e+04-3.9268e+03-1.3351e+04
4-3.1259e+04-3.9268e+03-1.2566e+04
5-3.1259e+04-3.9268e+03-1.1780e+04
6-3.1259e+04-3.9268e+03-1.0995e+04
7-3.1259e+04-3.9268e+03-1.0210e+04
8-3.1259e+04-3.9268e+03-9.4242e+03
9-3.1259e+04-3.9268e+03-8.6389e+03
10-3.1259e+04-3.9268e+03-7.8535e+03
11-3.1259e+04-3.9268e+03-7.0682e+03
12-3.1259e+04-3.9268e+03-6.2828e+03
13-3.1259e+04-3.9268e+03-5.4975e+03
14-3.1259e+04-3.9268e+03-4.7121e+03
15-3.1259e+04-3.9268e+03-3.9268e+03
16-3.1259e+04-3.9268e+03-3.1414e+03
17-3.1259e+04-3.9268e+03-2.3561e+03
18-3.1259e+04-3.9268e+03-1.5707e+03
19-3.1259e+04-3.9268e+03-7.8535e+02
20-3.1259e+04-3.9268e+03 5.5511e-12
'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "

edi2

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
u1 v1
03.1259e-047.5161e-03
12.9696e-048.3527e-03
22.8133e-049.0027e-03
32.6570e-049.4761e-03
42.5007e-049.7825e-03
52.3444e-049.9319e-03
62.1881e-049.9341e-03
72.0318e-049.7988e-03
81.8755e-049.5359e-03
91.7193e-049.1552e-03
101.5630e-048.6665e-03
111.4067e-048.0796e-03
121.2504e-047.4044e-03
131.0941e-046.6506e-03
149.3777e-055.8282e-03
157.8148e-054.9468e-03
166.2518e-054.0163e-03
174.6889e-053.0466e-03
183.1259e-052.0474e-03
191.5630e-051.0286e-03
200.0000e+003.4694e-18
" + ], + "text/plain": [ + "'\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
u1 v1
03.1259e-047.5161e-03
12.9696e-048.3527e-03
22.8133e-049.0027e-03
32.6570e-049.4761e-03
42.5007e-049.7825e-03
52.3444e-049.9319e-03
62.1881e-049.9341e-03
72.0318e-049.7988e-03
81.8755e-049.5359e-03
91.7193e-049.1552e-03
101.5630e-048.6665e-03
111.4067e-048.0796e-03
121.2504e-047.4044e-03
131.0941e-046.6506e-03
149.3777e-055.8282e-03
157.8148e-054.9468e-03
166.2518e-054.0163e-03
174.6889e-053.0466e-03
183.1259e-052.0474e-03
191.5630e-051.0286e-03
200.0000e+003.4694e-18
'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "

es3

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
N Vy Mz
0-3.9268e+03-2.8741e+04-8.1523e+03
1-3.9268e+03-2.5741e+04 1.9953e+01
2-3.9268e+03-2.2741e+04 7.2922e+03
3-3.9268e+03-1.9741e+04 1.3664e+04
4-3.9268e+03-1.6741e+04 1.9137e+04
5-3.9268e+03-1.3741e+04 2.3709e+04
6-3.9268e+03-1.0741e+04 2.7381e+04
7-3.9268e+03-7.7409e+03 3.0154e+04
8-3.9268e+03-4.7409e+03 3.2026e+04
9-3.9268e+03-1.7409e+03 3.2998e+04
10-3.9268e+03 1.2591e+03 3.3070e+04
11-3.9268e+03 4.2591e+03 3.2243e+04
12-3.9268e+03 7.2591e+03 3.0515e+04
13-3.9268e+03 1.0259e+04 2.7887e+04
14-3.9268e+03 1.3259e+04 2.4359e+04
15-3.9268e+03 1.6259e+04 1.9932e+04
16-3.9268e+03 1.9259e+04 1.4604e+04
17-3.9268e+03 2.2259e+04 8.3762e+03
18-3.9268e+03 2.5259e+04 1.2484e+03
19-3.9268e+03 2.8259e+04-6.7793e+03
20-3.9268e+03 3.1259e+04-1.5707e+04
" + ], + "text/plain": [ + "'\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
N Vy Mz
0-3.9268e+03-2.8741e+04-8.1523e+03
1-3.9268e+03-2.5741e+04 1.9953e+01
2-3.9268e+03-2.2741e+04 7.2922e+03
3-3.9268e+03-1.9741e+04 1.3664e+04
4-3.9268e+03-1.6741e+04 1.9137e+04
5-3.9268e+03-1.3741e+04 2.3709e+04
6-3.9268e+03-1.0741e+04 2.7381e+04
7-3.9268e+03-7.7409e+03 3.0154e+04
8-3.9268e+03-4.7409e+03 3.2026e+04
9-3.9268e+03-1.7409e+03 3.2998e+04
10-3.9268e+03 1.2591e+03 3.3070e+04
11-3.9268e+03 4.2591e+03 3.2243e+04
12-3.9268e+03 7.2591e+03 3.0515e+04
13-3.9268e+03 1.0259e+04 2.7887e+04
14-3.9268e+03 1.3259e+04 2.4359e+04
15-3.9268e+03 1.6259e+04 1.9932e+04
16-3.9268e+03 1.9259e+04 1.4604e+04
17-3.9268e+03 2.2259e+04 8.3762e+03
18-3.9268e+03 2.5259e+04 1.2484e+03
19-3.9268e+03 2.8259e+04-6.7793e+03
20-3.9268e+03 3.1259e+04-1.5707e+04
'" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "

edi3

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
u1 v1
07.5357e-03-2.8741e-04
17.5347e-03-1.9218e-03
27.5337e-03-3.5566e-03
37.5328e-03-5.1312e-03
47.5318e-03-6.5927e-03
57.5308e-03-7.8952e-03
67.5298e-03-9.0009e-03
77.5288e-03-9.8789e-03
87.5279e-03-1.0506e-02
97.5269e-03-1.0868e-02
107.5259e-03-1.0954e-02
117.5249e-03-1.0766e-02
127.5239e-03-1.0310e-02
137.5229e-03-9.6000e-03
147.5220e-03-8.6584e-03
157.5210e-03-7.5143e-03
167.5200e-03-6.2048e-03
177.5190e-03-4.7743e-03
187.5180e-03-3.2745e-03
197.5171e-03-1.7650e-03
207.5161e-03-3.1259e-04
" + ], + "text/plain": [ + "'\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n
u1 v1
07.5357e-03-2.8741e-04
17.5347e-03-1.9218e-03
27.5337e-03-3.5566e-03
37.5328e-03-5.1312e-03
47.5318e-03-6.5927e-03
57.5308e-03-7.8952e-03
67.5298e-03-9.0009e-03
77.5288e-03-9.8789e-03
87.5279e-03-1.0506e-02
97.5269e-03-1.0868e-02
107.5259e-03-1.0954e-02
117.5249e-03-1.0766e-02
127.5239e-03-1.0310e-02
137.5229e-03-9.6000e-03
147.5220e-03-8.6584e-03
157.5210e-03-7.5143e-03
167.5200e-03-6.2048e-03
177.5190e-03-4.7743e-03
187.5180e-03-3.2745e-03
197.5171e-03-1.7650e-03
207.5161e-03-3.1259e-04
'" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ed = cfc.extract_ed(edof, a)\n", + "\n", + "es1, edi1, ec1 = cfc.beam2s(ex1, ey1, ep1, ed[0, :], eq1, nep=21)\n", + "es2, edi2, ec2 = cfc.beam2s(ex2, ey2, ep1, ed[1, :], eq2, nep=21)\n", + "es3, edi3, ec3 = cfc.beam2s(ex3, ey3, ep3, ed[2, :], eq3, nep=21)\n", + "\n", + "cfu.disp_h2(\"es1\")\n", + "cfu.disp_array(es1, [\"N\", \"Vy\", \"Mz\"])\n", + "cfu.disp_h2(\"edi1\")\n", + "cfu.disp_array(edi1, [\"u1\", \"v1\"])\n", + "cfu.disp_h2(\"es2\")\n", + "cfu.disp_array(es2, [\"N\", \"Vy\", \"Mz\"])\n", + "cfu.disp_h2(\"edi2\")\n", + "cfu.disp_array(edi2, [\"u1\", \"v1\"])\n", + "cfu.disp_h2(\"es3\")\n", + "cfu.disp_array(es3, [\"N\", \"Vy\", \"Mz\"])\n", + "cfu.disp_h2(\"edi3\")\n", + "cfu.disp_array(edi3, [\"u1\", \"v1\"])\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Draw deformed frame" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "sfac=54.77300198398877" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plotpar = [2, 1, 0]\n", + "sfac = cfv.scalfact2(ex3, ey3, edi3, 0.1)\n", + "cfu.disp(f\"sfac={sfac}\")\n", + "\n", + "cfv.figure(1, fig_size=(10, 10))\n", + "cfv.eldraw2(ex1, ey1, plotpar)\n", + "cfv.eldraw2(ex2, ey2, plotpar)\n", + "cfv.eldraw2(ex3, ey3, plotpar)\n", + "\n", + "plotpar = [1, 2, 1]\n", + "cfv.dispbeam2(ex1, ey1, edi1, plotpar, sfac)\n", + "cfv.dispbeam2(ex2, ey2, edi2, plotpar, sfac)\n", + "cfv.dispbeam2(ex3, ey3, edi3, plotpar, sfac)\n", + "cfv.axis([-1.5, 7.5, -0.5, 5.5])\n", + "plotpar1 = 2\n", + "cfv.scalgraph2(sfac, [1e-2, 0.5, 0], plotpar1)\n", + "cfv.title(\"Displacements\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Draw normal force diagram" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plotpar = [2, 1]\n", + "sfac = cfv.scalfact2(ex3, ey3, es3[:, 1], 0.2)\n", + "cfv.figure(3, fig_size=(10, 10))\n", + "cfv.secforce2(ex1, ey1, es1[:, 1], plotpar, sfac)\n", + "cfv.secforce2(ex2, ey2, es2[:, 1], plotpar, sfac)\n", + "cfv.secforce2(ex3, ey3, es3[:, 1], plotpar, sfac)\n", + "cfv.axis([-1.5, 7.5, -0.5, 5.5])\n", + "cfv.scalgraph2(sfac, [3e4, 0.5, 0], plotpar1)\n", + "cfv.title(\"Shear force\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Draw shear force diagram" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "sfac=3.628630851048567e-05" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkkAAAJOCAYAAACjhZOMAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAAsTAAALEwEAmpwYAAA6lUlEQVR4nO3dd3yV5f3/8feHhJkTRGQqILSgFhyoiAPrQKniFid10P6sfL/u2X7bilqts8MOR5WKe+CeVdzWrRXFiloVrQOaACpDkZlcvz+upAS4Q06Sc851j9fz8bgfSU6Sw/sAOeed67ru6zbnnAAAALCqNqEDAAAAxBElCQAAIAIlCQAAIAIlCQAAIAIlCQAAIAIlCQAAIAIlCQAAIAIlCUDBmNknZrbMzLqtdvs0M3Nm1j9QtDWY2Q1mdkHoHADii5IEoND+LWls/QdmtpmkjuHiAEDLUJIAFNrNko5u8PE4STfVf2Bm65jZTWY218w+NbMJZtam7nM/MrMXzewPZjbfzD42sx3qbv/czOaY2bgG99XezH5nZp+Z2Wwzu9rMOtZ9bhczm2lmZ9R9X5WZ/bjuc+MlHSHpZ2b2jZk9VIK/FwAJQ0kCUGivSOpsZt8zszJJh0m6pcHnL5e0jqTvSNpZvlD9uMHnt5X0T0nrSbpN0mRJ20gaKOlISVeYWa7uay+VtJGkoXWf30DSOQ3uq1fdn7WBpGMkXWlm6zrnJkq6VdJvnHM559y+hXnoANKEkgSgGOpHk0ZJ+pekWXW315emXzjnvnbOfSLp95KOavC9/3bOXe+cq5F0h6S+ks53zi11zj0uaZmkgWZmko6VdJpz7ivn3NeSLpJ0eIP7Wl73vcudc49I+kbSxsV5yADSpjx0AACpdLOk5yQNUIOpNkndJLWT9GmD2z6VH+mpN7vB+4slyTm3+m05Sd0ldZI01fclSZLJF7F6XzrnVjT4+Nu67wWAJjGSBKDgnHOfyi/g3kvSvQ0+9YX86M6GDW7rp5UjTc3xhXxhGuKc61J3rOOcy7cEuRb8mQAyhJIEoFiOkTTSObeowW01ku6UdKGZVZrZhpJO16prlvLinKuV9FdJfzCzHpJkZhuY2R553sVs+XVRABCJkgSgKJxzHznnXo/41EmSFkn6WNIL8ouzr2vhH/N/kmZIesXMFkp6UvmvOZokaXDdWXT3t/DPB5Bi5hwjzgAAAKtjJAkAACACJQkAACACJQkAACACJQkAACBCUTaT7Natm+vfv38x7hoAAKCgpk6d+oVzrvvqtxelJPXv31+vvx515i8AAEC8mNmnUbcz3QYAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABChPJ8vMrNPJH0tqUbSCufcsGKGAgAACC2vklRnV+fcF0VLAgAAECNMtwEAAETItyQ5SY+b2VQzG1/MQAAAAHGQ73TbCOfcf8ysh6QnzOxfzrnnGn5BXXkaL0n9+vUrcEwAAIDSymskyTn3n7q3cyTdJ2l4xNdMdM4Nc84N6969e2FTAgAAlFiTJcnMKsyssv59ST+QNL3YwQAAAELKZ7qtp6T7zKz+629zzk0paioAAIDAmixJzrmPJW1RgiwAAACxwRYAAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAADExIIFoROgIUoSAAAxMGuWtMkmklnoJKhHSQIAIAZOPlk69tjQKdAQJQkAgMDuv1+aPl365S9DJ0FD5aEDAACQZQsXSiedJN1yi9ShQ+g0aIiRJAAAAvrlL6U99pB23jl0EqyOkSQAAAJ5+WXp3nuld94JnQRRGEkCACCAZcuk8eOlP/xBWnfd0GkQhZIEAEAAv/ud1K+fdOihoZOgMUy3AQBQYh98IF12mTR1KvsixRkjSQAAlJBz0v/8jzRhgrThhqHTYG0oSQAAlNANN0jffONP+0e8Md0GAECJzJkj/fzn0pQpUllZ6DRoCiNJAACUyKmnSuPGSVtuGToJ8sFIEgAAJfDoo9Krr0rXXhs6CfJFSQIAoMgWLZKOP1665hqpU6fQaZAvptsAACiyc8+VdtxR+sEPQidBczCSBABAEU2dKt18szR9eugkaC5GkgAAKJIVK6Rjj5V++1upe/fQadBclCQAAIrkT3+SunaVjjoqdBK0BNNtAAAUwb//LV18sfTKK1x6JKkYSQIAoMCck447TjrzTGngwNBp0FKUJAAACuz226WqKumMM0InQWsw3QYAQAF9+aUvRw88ILVtGzoNWoORJAAACuinP5UOPVQaPjx0ErQWI0kpMH26tOmmoVMAAJ5+WnrqKfZESgtGkhJu+XJpzBjOnACA0BYvlv7nf6QrrpAqK0OnQSFQkhKubVvpppv8+1VVYbMAQJZdcIG05ZbSvvuGToJCoSSlwHbb+bc/+Yk/7RQAUFpvvy1NnOg3j0R6UJJSpLra/5ACAEqnpsZfeuTCC6XevUOnQSFRklLk5puls86SZswInQQAsuPKK6V27fxoPtKFkpQigwdLZ58tHX20v6giAKC43n1X+vWvpeefl9rwipo6/JOmzEknSZ06Sb/5TegkAJBuy5ZJRxwhXXRR6CQoFkpSyrRpI11/vfTHP0pvvhk6DQCk19lnS/36Mc2WZmwmmUJ9+0qXXSYdeaQ0darUoUPoRACQLs8+69eBvvUW+9SlGSNJKXXEEdKQIdIvfxk6CQCky/z50rhx0qRJUvfuodOgmChJKWUm/eUv0h13SM88EzoNAKTH8cf7DSNHjw6dBMXGdFuKrbeedO210o9+JP3zn9I664ROBADJdttt0rRp0uuvh06CUmAkKeVGj5b22ks6+eTQSQAg2T79VDr1VOmWW/xZxEg/SlIG/O530ksvSffcEzoJACRTTY3fg+6MM6SttgqdBqVCScqAigp/FsYJJ3ARXABoid/+1q/1PPPM0ElQSpSkjNhuO39tIS6CCwDN88YbfluVm26SyspCp0EpUZIy5Jxz/EVw//rX0EkAIBm+/dZvqfKnP/mNI5EtlKQMaduWi+ACQHP89Kd+DdLYsaGTIARKUsYMHixNmMBFcAGgKY88Ij38sHTllaGTIBRKUgZxEVwAWLs5c/wazptukrp0CZ0GoVCSMoiL4AJA45zzBenoo6Wddw6dBiFRkjKqb1/pD3/wF8FdsiR0GgCIj4kTpZkzpfPPD50EoVGSMuyHP/QXwT3rrNBJACAe3n/fPyfeeqvUrl3oNAiNkpRh9RfBnTyZi+ACwPLlfnT9vPOk730vdBrEQd4lyczKzOxNM3u4mIFQWg0vgrtgQeg0ABDOeedJ3btLxx8fOgniojkjSadIeq9YQRDO6NHS3ntzEVwA2fXCC9KkSdJ11/lRdkCSyvP5IjPrI2lvSRdKOr2oiTJi5kzprbd8OWktq/uJLsQP9k03tf4+ACCpevdu+fcWtlyFv37U88/72YbBg0MnCSevkiTpj5J+JqmysS8ws/GSxktSP/Zub9KMGdI++4ROAQCIoziNZmX5ep9NliQz20fSHOfcVDPbpbGvc85NlDRRkoYNG5bhv9L85HJ+q/upU1t/X/U/TK4A/5OvuMKvUXrpJb/hZGtzFeKHq1D3E9f7IlPp74tMpb+vuGa64w5/FYI335QqKlp/n63P5J/Q41BMRo2SzjwzdIqw8lmTNELSfmb2iaTJkkaa2S1FTZUBlZXSN9+ETrGmE07w2wIcf3w8fkgBoJhOPNGf7h+HghQ3VVWtm35MgyZLknPuF865Ps65/pIOl/S0c+7IoidLuVxO+vrr0CnWZOY3Ups61b8FgDSqrfVvTz5Z2mabsFniipKU/5okFFguF8+RJMn/RnXPPdKOO/opQZ5AAKTNeef5tz//edgccbV0qf9Ffr31QicJq1mbSTrnnnXOsdy4ACoqpEWL4jultdFG0jXXSAcfLH3xReg0AFA4Dz7oT/WvqpLKGSqIVF0t9ezpr/WZZRl/+OGUl0vt20uLF4dO0rgDD5TGjvWXL6mpCZ0GAFrv/ff9xWvvukvq1St0mvhiqs2jJAUU13VJDV1wgbRihfSrX4VOAgCt8/XX/pe/Cy6QttsudJp4oyR5lKSA4rwuqV55ub+22w03SA89FDoNALSMc/7ySyNGSOPHh04Tf5Qkj5IUUBJKkiT16CHdead0zDHSRx+FTgMAzXfJJf5KB1dcETpJMlRVMR0pUZKCiuteSVG231465xzpoIOkb78NnQYA8vfYY9Lll/uzdtu3D50mGRhJ8ihJASVhTVJDbDQJIGk+/lg6+mi/bKBPn9BpkoOS5FGSAkrKdFs9NpoEkCTffiuNGSOddZa0006h0yQLJcljh4iAklaSpFU3mtxyS2n48NCJAGBNzknHHitttpl00kmh0yQPJcljJCmgJK1JamijjfxI0iGHsNEkgHj605+k997zm+LWXwQc+amp8c/tPXuGThIeJSmgpK1JauiAA/xGk2PHstEkgHh55hl/Ntu990qdOoVOkzxz50rrriu1bRs6SXiUpICSON3W0AUX+IJ07rmhkwCA9/nn/ioBt9wi9e8fOk0yMdW2EiUpoKSXpPqNJm+8kY0mAYS3ZIlfqH3aadLuu4dOk1yUpJVYuB1QZaX05z/7I6nqN5rcf3/ppZekgQNDJwKQRc75bUoGDJB++tPQaVqPdVTxQEkKKJfzi5/vvLN19xP6h6nhRpMvv8waAACld8010quvSq+8Ev45sRBC7kV3wQVsGlyP6baAkj7d1tAJJ/hTbY87jo0mAZTWSy/5X9Tuv98/r6J1mG5biZIUUJpKkpn/Te6NN/xbACiFqirp0EOl669nur9QKEkrMd0WUFL3SWpMRYU/5XbECGmrrUKnAZB2y5ZJBx8sjR8v7b136DTpQUlaiZGkgJK8T1JjBg1audEkABTTaadJ3bpJEyaETpIulKSVKEkBpWm6raEDDvAXlJSkRYuCRgGQUtdfLz31lHTTTVIbXskKxjmpupqSVI/ptoDSWpIk6fzzpc8+kw4/XLrvPr+nEgAUys9+Jj33nLTOOqGTpMu8eVKHDlLHjqGTxAP9O6CKCn+aZW1t6CSFZyZde61fM3DCCZzxBqAw5szxb6+5Rvre98JmSSOm2lZFSQqorMw39rTuR9G2rXT33dI//iFdeGHoNACSbsmSlesdx4wJmyWtKEmrYhIksPopt7Tu7VFZKT3yiLTDDtIGG0g//nHoRACSqKZGOuIIqVev0EnSjZK0KkaSAkvzuqR6vXpJjz4q/eIX0pQpodMASBrnpOOPlxYs8Au1mb4vHkrSqihJgaVtr6TGbLyx30Pp6KOlqVNDpwGQJL/6lX/euO8+qX370GnSjZK0KkpSYGncK6kxO+zg91Dabz/p449DpwGQBFddJd12m5+2r6wMnSb9KEmrYk1SYFmYbmvogAOk//xH2nNPf72lbt1CJwIQV3fdJV10kfT881KPHqHTZAMlaVWMJAWWtZIk+bUFBx0k7btves/sA9A6Tz/ttw/529+kAQNCp8kOStKqKEmBZWVN0uouushfwuSHP/RnrQBAvTfe8BvR3nWXtMUWodNkCyVpVZSkwLK0Jqmh+s0mFy2STjqJs1UAeDNmSPvsI119tbTzzqHTZMs33/hfWjt3Dp0kPihJgeVy0imn+NLQ0iOp2rWT7rlHevll6eKLQ6cBEFp1tbTHHv5stqxvFtma14SWHpWV0uLFyX5dKTRKUmC5nN8/yLmWH0nWubNfc/DXv/r9TwBk04IF/oSOH/1IGj8+dJrwWvOa0NLj73+XRowI/cjjhbPbAquslGbPDp0irPXX96f37rqr33jyBz8InQhAKS1Z4s983XFHacKE0Gmyi/VIa2IkKbCsrkla3fe+56fejjzSL9oEkA01Nf7nvnt36U9/YqonJErSmihJgWVxC4DGjBjhF2vuu6/0ySeh0wAoNuekE0+U5s2Tbr7ZX/Qb4VCS1sR0W2CUpFWNGbNys8kXX5TWWy90IgDFcv750muvSc88w+VG4qCqyo/qYyVKUmBZ3SdpbU48Ufr8c3/5kiefDJ0GQDFcfbUfPXrxRU45jwtGktbEdFtgrEmKdvHFfpfdH/4wdBIAhXb33dKvfy09/rjUs2foNKhXVeVPnsFKlKTAmG6L1qaNdN11Kwtk0rc6AOA984y/NNHf/iZ95zuh06AhRpLWREkKjJLUuHbtpHvv9e+feKJUWxs2D4DWefNN6bDDpDvvlIYODZ0GDS1d6n8p5aLjq6IkBcaapLXr3NlvMvfPf0rHHst13oAk23tv6S9/kXbZJXQSrK66WurRw4/iYyX+OgLr1En69ltGSdamc2dpyhS/LcC4cdKKFaETAWiO6mr/9pxzpIMOCpsF0Zhqi0ZJCqxNG1+UFi0KnSTeKiqkhx+WvvxSGjtWWrYsdCIA+fj0U2mnnfz7//u/YbOgcZSkaJSkGGBdUn46dpTuv19avlw6+GB/KQMA8fX++74gnXACJ1/EXXU1JSkKJSkGWJeUv/btpbvukjp0kPbf309VAoifadP89Rh/9SvplFNCp0FTGEmKRkmKAfZKap62baXbbvP7q+y9NwUTiJuXXpL22EP685+lH/84dBrkg5IUjZIUA0y3NV95uXTDDdLAgf7JeMGC0IkASH6X/P33l2680U+LIxkoSdEoSTFASWqZNm2ka66RttpK2n136auvQicCsu2BB/wu+ffe66+/iOSgJEWjJMVAZaWfNjJr2ZFlbdr4If1ddpFGjpTmzg2dCMimW27xZ689+qj0/e+HTpN8LX09aOnxxhvS8OGhH3X8UJJiIJeTrr3Wn/3RkiPrzKTf/MZfEHeXXfxvRABK56qrpF/8QnrqKWnrrUOnSYeWvh605Fixwi9hYGuVNZWHDgCm2wrBTDr/fH/22847+yfrvn1DpwLS75JLpL/+VXruOX9RaiTP3LnSuuv6k2KwKkpSDFCSCuess/x+SvVFiSdtoDic86NHDz0kPf+8tP76oROhpViP1DhKUgxUVkoLF4ZOkR6nn+73UdplF3+mzaBBoRMB6VJb6y86/Y9/SH//OxdFTTpKUuMoSTGQy0mzZoVOkS7HH++n3nbdVXr8cWnw4NCJgHRYscLvffTZZ360tnPn0InQWpSkxlGSYoDptuI45hhflHbbzV8gd4stQicCkm3JEunww/0C30cf9dedRPJVVUm9eoVOEU+UpBigJBXPkUf6qbc99vAXyAXQMt98Ix1wgNS1q3TnnVK7dqEToVCqqqSNNw6dIp4oSTHAtduK6+CD/YjSXnuFTgIk07x5fi+3wYP9Bq5lZaEToZCqqvwaTqyJfZJigGu3Fd+++0q33urfnzgxbBYgaXbdVdpuO3+qPwUpfViT1DhKUgww3VYao0ZJ778v/fGP0nHHsXEa0JTPPvNvDzxQ+v3v2eE/rShJjaMkxQAlqXQ22kh65RV/NuFuu0mzZ4dOBMTT66+vvLzIuedSkNLKOam6mpLUGEpSDLAmqbQ6d5buv99PIWyzjX8xALDSpEl+Dd9ll4VOgmKbN8+v2eRMxWgs3I4B1iSVXps2/jImQ4dKo0f7KbgjjgidCghryRLppJOkF1/0lxnZZBOuD5l2jCKtHSUpBjp29E9ONTUsiiy1MWP8FNwBB/irYF96qb/QI5A1n37qzwQdMEB69VU/wo30Yz3S2jHdFgNt2kgVFdKiRaGTZNOmm0qvvSa9/bafYvjqq9CJgNJ64glp2239RpF33EFByhJK0tpRkmKCdUlhde0qPfKItPnmfp3S9OmhEwHFV1srXXSRNG6cNHmydMYZLNDOGkrS2jGxEBOsSwqvvFz63e/8OqVdd/Wb5o0ZEzoVUBwLFvhyNGeOv1DtBhuEToQQqqqk9dcPnSK+GEmKCUpSfBx5pL/W22mnSeec43/bBtJk+nQ/Ytqnj/TssxSkLGMkae0oSTGRy/knLbPmHyi8rbf265SeecZvpLdwYehEQGHcfrsfKT37bOmKK7gGW1y15LWgJcfkyf4XQ0SjJMVEZaW/AKtzzT9QHD17Sk895X/L3m476YMPQicCWm75cunUU6UJE6Qnn5SOOip0IqxNS14LWnJstJH07ruhH218UZJigum2eGrXTrrqKj/19v3vS48+GjoR0HxVVdLIkdKMGX7z1C22CJ0IccF029pRkmKCS5PE27HHSvfeK/3kJ9IllzCCh+R44QVp2DB/7cIHH5TWXTd0IsTFokV+hHGddUIniS/ObosJSlL8jRjhN9kbM0aaNi10GmDtnJMuv1y68ELphhv8zvJAQ/WjSKxtbRwjSTHBPknJ0KePv1xD167+44ceCpsHaMwRR0jXXy+9/DIFCdGYamsaJSkmWJOUHB06+HVKzz4rnX66NHasNHdu6FSA9/77/m27dtJLL0nf+U7YPIgvSlLTKEkxwXRb8uy8s/TWW1LfvtJmm0m33spaJYSzdKn061/7aWHJjyJ17Bg2E+KNktQ0SlJMUJKSqVMn6Te/8ds3XHqptO++0uefh06FrPn73/0Za1On+gs1O8c6EzSNktQ0SlJMsCYp2YYN86dWb7edtNVW0tVXs1M3iu+LL6Qf/9jveXTJJdL990v9+oVOhaSgJDWNkhQTrElKvnbt/EZ9f/+7dOONfldjNqBEMTjn/49tuqnUpYv0zjvSAQeEToWkoSQ1jZIUE0y3pcfgwX5vmjFjpB128NNxK1aEToW0eP99vzHk5ZdLjzwi/eEPfiQaaC5KUtMoSTFBSUqXsjLplFP81dWfeELadlu/yBtoqSVLpF/9StpxR1/AX33VT+0CLUVJaholKSZYk5ROAwZIjz8unXii3/F4wgT/Ygc0x9NPS5tvLr39tvTmm9JJJ/kiDrTUsmX+wt3duoVOEm+UpJhgTVJ6mfnFtW+95S8kueWWfv8aoClz50pHH+3///z+99I99/gNTYHWqq6WevSQ2tAC1qrJvx4z62Bmr5nZW2b2jpmdV4pgWcN0W/r17u2v/3bBBdLBB0snn8y/OaLV1kqTJvmF2T16+IXZ++4bOhXShKm2/ORz7balkkY6574xs7aSXjCzR51zrxQ5W6Z07OiHP1eskMq5ol6qHXSQP/Pt9NP9JpQS+9pgpXfflf73f/3mkI89Jg0dGjoR0oiSlJ8mR5KcV//7btu6g32FC8yM0aQs6drVX3T06qv9x8OH+yu0s2N3tk2Y4HdyP+wwPyVLQUKxUJLyk9eYhZmVSZoqaaCkK51zr0Z8zXhJ4yWpH7uZtUguJ627bugUKKU99pBqaqT77pPOOUc691z/QnnggawVyIrly6Xbb/fvf/CBX7u2/vphMyG8Uo0sX3NNaf6cpMrradg5V+OcGyqpj6ThZrZpxNdMdM4Nc84N6969e4FjZkMuJ733nh9NaM6BZGvTxk/BvfmmdN55fufkzTeXJk/2BQrp9M03fo+j737XbwwpSXfeSUGC19zXgeYeP/nJypFsNK5Zv6s65+ZLelbSnsUIk3VMt2WbmbTfftJrr0m//a305z9LQ4ZIN9/MZpRpMmeOdPbZfnuIl1/2i/mfeopfeFBaTLflJ5+z27qbWZe69ztK2l3Sv4qcK5PYKwmSL0ujR0svvihdeaV07bXSJptI113np2aQTB9/LJ1wgv+3nDvXF6Q77/TX/QNKjZKUn3xGknpLesbM/inpH5KecM49XNxY2cRIEhoyk3bbzV8LbtIk6bbbpEGD/BD50qWh0yFfb74pjR3rF+evs44/e+3qq6WBA0MnQ5ZRkvKTz9lt/3TObemc29w5t6lz7vxSBMsiNpREY3beWXrySV+UHnjAv8Befrm0eHHoZIjinJ9C22MPv7/R1lv7kaSLLpJ69QqdDllXU+NHM3v0CJ0k/jh/JkYYSUJTdthBevRRv47liSf8ot/LLpMWLQqdDJJ/8bnrLmmbbfylaA47TProI+nMM6XOnUOnA7y5c6UuXaR27UIniT9KUoywJgn52mYbv6/SI4/4/XS++13p0ktDp8quJUv8qdSbbOLPWDv7bL9L9v/7f1L79qHTAatiqi1/lKQYYSQJzTV0qHT33X4q7u23/W2jRkkTJ/rfFlFc8+f7twMGSA895BfXv/iitP/+7HOF+KIk5Y8f4xihJKGlNt1UuuUW//9n/Hi/HmbgQGn33f0i4TlzQidMj5kzpauukvbcU+rb19/2xBPSww9L3/8+l5dB/FGS8kdJihEWbqO1KiqkQw6R7rjDPxEed5w/O26jjaSRI/2Le3V16JTJ4tzKjT633lraYgt/+v4xx0izZvnPb7rG9rpAfFGS8selVGOENUkopE6d/E7eBx3kz4KbMsVPzf3yl36a7pBDpDFjeLKMsnSpL5cPPuiPdu38FNpll0kjRnARaiRbVZW08cahUyQDP+oxwnQbiqVjR389uAMP9IuMH3vMF6YJE6TNNltZmDbYIHTScL76yi+Ef/BB6fHHpcGD/Q7ojz3mF2QzjYa0qKqSdtkldIpkoCTFCCUJpdChgx8V2X9/P2Ly+OO+MJ17ri8Ghxziv8659BeDjz7ypeiBB6Q33vBTkvvt5/eg6tkzdDqgOKqrGUHOFyUpRihJKLX27f1mh/vu6wvTU0/5fX4kab31/FqbhseQIf72JFq61JeiDz7wHw8ZIn35pX/sZ5zhdzfv1ClsRqAUWJOUP0pSjFRWsnAb4bRvL+21lz+uv96fETd9uj+mTfNnz02f7sv86uVp8GB/e2grVkiffCJ9+KEvQw3fVlVJG27oL+0i+dP1t9mGU/WRLc4xktQclKQYYSQJcdKjh59+Gjly5W3OSZ9/vrI8PfusdMUV0r/+5S+3sXp5Ksbi0Npafxp+wwJU//6nn/on/0GD/Bl9gwb50rfRRlL//iy4BubP9yciMGqaH54yYoQtABB3ZlK/fv7Ya6+Vt9fU+GuT1ZenBx6QLrzQ3yb54lJW5ktKWdnKo+HHjb1f/7Fz/r5yOX9JhfoStNFG0k47+bff+Y5fcwUgGlNtzUNJipFczq+RSPtiWaRPWZkvLIMG+TPo6i1d6kvL1Km+SNXU+CmxqPeb+pxzfsPG2bP91DSQZrwOxAMlKUY6dPAvNkuWNG9agB8mxFX79itHgAqhkPcFxFmx/q/fcovf6gL5YclijJixLgkAUDxMtzUPJSlmKEkAgGKhJDUPJSlmKEkAgGKhJDUPJSlmuH4bAKBYKEnNQ0mKGbYBAAAUS1WV39MM+aEkxQzTbQCAYmEkqXkoSTFDSQIAFMOiRdKyZX4zVuSHkhQzrEkCABRD/SgSe+vlj5IUM6xJAgAUA1NtzUdJihmm2wAAxUBJaj5KUsxQkgAAxUBJaj5KUsxUVjLdBgAovOpqSlJzUZJihpEkAEAxMJLUfJSkmKEkAQCKgZLUfJSkmKEkAQCKgZLUfJSkmGFNEgCgGChJzVceOgBWlctJr77KZl8AkGXFeg3o1Utyrjj3nUaMJMVMLidtuKH/T5zvAQBIl+a8BuRzfPaZtP76vGY0FyUpZliTBAAoNKbaWoaSFDOsSQIAFBolqWUoSTHTrp1UW+uv1AwAQCFQklqGkhQzZn7KbdGi0EkAAGlBSWoZSlIMsS4JAFBIlKSWoSTFEOuSAACFRElqGUpSDDGSBAAoJEpSy1CSYoiSBAAoJEpSy1CSYoiSBAAolJoaae5cqWfP0EmSh5IUQ6xJAgAUyhdfSOus47eYQfNQkmKIkSQAQKFUVzPV1lKUpBiiJAEACoX1SC1HSYohShIAoFAoSS1HSYoh1iQBAAqFktRylKQYYiQJAFAolKSWoyTFECUJAFAolKSWKw8dAGvK5aRbb/UHACB7zAp7f/fcIzlX2PvMAkaSYqiyUho50v+HzucAAKRLvs//+RwDBkgzZoR+RMlESYohptsAAIXgHNNtrUFJiiFKEgCgEBYs8Dttd+oUOkkyUZJiiJIEACgERpFah5IUQ+yTBAAohKoqqVev0CmSi5IUQ4wkAQAKgZGk1qEkxVD9lZqXLQubAwCQbJSk1qEkxVRlJaNJAIDWoSS1DiUppnI51iUBAFqHktQ6lKSYYl0SAKC1KEmtQ0mKKUoSAKC1KEmtQ0mKKdYkAQBai5LUOpSkmGJNEgCgNb791p8l3aVL6CTJRUmKKabbAACtUb+RpFnoJMlFSYopShIAoDWYams9SlJMsSYJANAalKTWoyTFFGuSAACtQUlqvfLQARAtl5POPFM6//zQSQAApVbIdURXXFG4+8oaRpJiKpeTxo+XnGv6AACkSz7P/U0d48ZJkyaFfiTJRkmKKdYkAQBag+m21qMkxRRrkgAArUFJaj1KUkyxBQAAoDUoSa1HSYopShIAoKWWLZPmz5e6dQudJNkoSTHFmiQAQEvNni117y6VlYVOkmyUpJhiTRIAoKWYaisMSlJMMd0GAGgpSlJhUJJiqr4ksQ8SAKC5KEmFQUmKqbZt/VzysmWhkwAAkoaSVBiUpBhjXRIAoCUoSYVBSYox1iUBAFqCklQYlKQYoyQBAFqiupqSVAiUpBhjryQAQEswklQYTZYkM+trZs+Y2Xtm9o6ZnVKKYGBNEgCg+WprpTlzpF69QidJvvI8vmaFpDOcc2+YWaWkqWb2hHPu3SJnyzym2wAAzfXFF1LnzlK7dqGTJF+TI0nOuSrn3Bt1738t6T1JGxQ7GChJAIDmY6qtcPIZSfovM+svaUtJr0Z8bryk8ZLUr1+/QmTLvMpK6eij/QEAyA6z0AkgNWPhtpnlJN0j6VTn3MLVP++cm+icG+acG9a9e/dCZsysXE669FK/6/baDgBAujT1vL+247rr+OW6UPIqSWbWVr4g3eqcu7e4kVCP6TYAQHMx3VY4+ZzdZpImSXrPOXdZ8SOhHiUJANBclKTCyWckaYSkoySNNLNpdcdeRc4FUZIAAM1HSSqcJhduO+dekMQSsgAqK9knCQDQPJSkwmHH7RhjJAkA0FyUpMKhJMUYJQkA0BzOUZIKiZIUY5QkAEBzLFggtW0rVVSETpIOlKQY49ptAIDmYBSpsChJMVZZyUgSACB/lKTCoiTFGNNtAIDmoCQVFiUpxioqfEni0iMAgHxQkgqLkhRjbdtK5eXSkiWhkwAAkqC6mpJUSJSkmGPKDQCQL0aSCouSFHOUJABAvihJhUVJijnOcAMA5IuSVFiUpJhjryQAQL4oSYXV5AVuEVYuJ40YEToFAKCUrBWXle/SpWAxMo+RpJjL5aR77vHbADR2AADSZW3P+Y0dH30k9e/fuoKFVVGSYo41SQCAfDDVVniUpJhjTRIAIB+UpMKjJMUcWwAAAPJBSSo8SlLMUZIAAPmgJBUeJSnmWJMEAMhHVZXUq1foFOlCSYo51iQBAPLBSFLhUZJijuk2AEA+KEmFR0mKOUoSACAflKTCoyTFHGuSAABNWb5cmjdP6t49dJJ0oSTFHGuSAABNmT3bF6SystBJ0oWSFHNMtwEAmsJUW3FQkmKOkgQAaAolqTgoSTHHmiQAQFMoScVBSYq5igpfkpwLnQQAEFeUpOKgJMVcebnUrp20eHHoJACAuKIkFQclKQFYlwQAWBtKUnFQkhKgspJtAAAAjaMkFUd56ABoWi4nDRwYOgUAoFTMmv89223H+tVCYyQpAXI56YUX/H/+qAMAkC6NPd9HHTU1Utu20tKloVOnDyUpAViTBABozBdfSJ07+5N8UFiUpARgryQAQGNYj1Q8lKQE4PptAIDGUJKKh5KUAEy3AQAaU1Ul9eoVOkU6UZISgJIEAGgMI0nFQ0lKANYkAQAaQ0kqHkpSArAmCQDQGEpS8VCSEoDpNgBAYyhJxUNJSgBKEgCgMZSk4qEkJQBrkgAAUZyTqqspScVCSUoA1iQBAKIsXCiVlfnXCRQeJSkBmG4DAERhqq24KEkJQEkCAEShJBUXJSkBWJMEAIhCSSouSlICsCYJABCFklRclKQEqKiQFi2SamtDJwEAxAklqbjKQwdA08rKpA4d/FsAQPqZNe/rf/az4uTIOkaSEqKy0u+F4dyaBwAgXaKe66OOXXeVnngidNr0oiQlBOuSAACrY7qtuChJCcE2AACA1VGSiouSlBCUJABAQ4sXS0uWSOuuGzpJelGSEoK9kgAADVVVSb16NX+RN/JHSUoI1iQBABpiqq34KEkJwXQbAKAhSlLxUZISgpIEAGiIklR8lKSEYE0SAKAhSlLxUZISgjVJAICGKEnFR0lKCKbbAAANUZKKj5KUEJQkAEBD1dWUpGKjJCUEa5IAAA0xklR8lKSEYE0SAKDeihXSvHlS9+6hk6QbJSkhmG4DANSbPVvq1k0qKwudJN0oSQlBSQIA1GOqrTQoSQlRWcl0GwDAoySVRnnoAMhPLid99BEXMgSALOC5Ph4oSQmRy0kVFdFTbvwwAUC6OLf2z593nlRTU5osWcZ0W0JUVEjffivV1oZOAgAIjem20qAkJUSbNlKnTr4oAQCyjZJUGpSkBGGvJACAREkqFUpSgrANAABAoiSVCiUpQShJAIDaWr+ZZM+eoZOkHyUpQbh+GwDgyy/960H79qGTpB8lKUFYkwQAYKqtdChJCcJ0GwCAklQ6lKQEoSQBAChJpUNJShCu3wYAoCSVDiUpQRhJAgBQkkqHkpQglCQAACWpdChJCUJJAgBQkkqnyZJkZteZ2Rwzm16KQGgca5IAAJSk0slnJOkGSXsWOQfywEgSAGSbc5SkUmqyJDnnnpP0VQmyoAlsJgkA2bZwoVRW5l8PUHzlhbojMxsvabwk9evXr1B3iwZyOenxxyWz0EkAAMXE83w8FGzhtnNuonNumHNuWPfu3Qt1t2igslLafHM/3NrwAACky+rP8/XH009LO+0UOl12cHZbgrAmCQCyjfVIpUVJShBKEgBkGyWptPLZAuB2SS9L2tjMZprZMcWPhSiUJADINkpSaTW5cNs5N7YUQdC0Tp2kJUukmhp/dgMAIFuqqvzaVJQG020J0qaNL0qLFoVOAgAIgZGk0qIkJQxTbgCQXZSk0qIkJQwlCQCyi5JUWpSkhOH6bQCQTYsX+6Nr19BJsoOSlDCMJAFANlVVSb16sRt3KVGSEoaSBADZxFRb6VGSEoaSBADZVF1NSSo1SlLCsCYJALKJkaTSoyQlDCNJAJBNlKTSoyQlDCUJALKJklR6lKSEoSQBQDZRkkqPkpQwrEkCgGyiJJUeJSlhcjnpqqv8Phn1BwAgXRo+x9cfb70lbb116GTZQklKmFxOGjNGcm7lAQBIl4bP8c5Jy5dL5eXSihWhk2ULJSlhWJMEANkze7bUrZtUVhY6SbZQkhKGNUkAkD2sRwqDkpQwjCQBQPZQksKgJCUMJQkAsqf+4rYoLUpSwlCSACB7GEkKg5KUMKxJAoDsoSSFQUlKmI4dpWXLOA0UALKEkhQGJSlhzKSKCmnRotBJAAClQkkKg5KUQKxLAoBsoSSFQUlKINYlAUB21Nb6zSQ5u630KEkJxEgSAGTHl1/65/0OHUInyR5KUgJRkgAgO6qrmWoLhZKUQJQkAMgO1iOFQ0lKINYkAUB2UJLCoSQlECNJAJAdlKRwKEkJREkCgOygJIVTHjoAmi+Xk04/3R8AgPQxW/O2004rfY6sYyQpgSorpZ/+VHLOHwCAdKl/fndO2nFH6dlnQyfKJkpSAjHdBgDZwXRbOJSkBKIkAUA2OEdJComSlECUJADIhq+/9uuTKitDJ8kmSlICsU8SAGQDo0hhUZISiJEkAMgGSlJYlKQEoiQBQDZQksKiJCUQJQkAsoGSFBYlKYFYkwQA2UBJCouSlECMJAFANlCSwqIkJVCHDtKKFdLy5aGTAACKiZIUFiUpgcz8aNKiRaGTAACKiZIUFiUpoXI51iUBQNpRksKiJCUU65IAIN0WL5a+/Vbq2jV0kuyiJCUUJQkA0q26WurVyy+xQBiUpISqrKQkAUCaMdUWHiUpoViTBADpRkkKrzx0ALRMLiftv3/oFACAYmCKLR4oSQmVy0nXXCONH88PEwCkjXPSWWf5ffEQDtNtCcWaJABIN6bbwqMkJRRrkgAg3ShJ4VGSEootAAAg3ShJ4VGSEoqSBADpRkkKj5KUUKxJAoD0WrFC+uorqXv30EmyjZKUUKxJAoD0mj1bWm89qZxz0IOiJCUU020AkF5MtcUDJSmhKEkAkF6UpHigJCUUa5IAIL0oSfFASUoo1iQBQHpRkuKBkpRQTLcBQHpRkuKBkpRQlCQASK/qakpSHFCSEqp9e6m2Vlq2LHQSAEChMZIUD5SkhDJjNAkA0oqSFA+UpASjJAFAOs2eLfXqFToFKEkJRkkCgHSqqJA6dAidApSkBKuslIYMCZ0CAFBo8+aFTgCJkpRouZz05JOhUwAACm233UIngERJSjSm2wAgnVi0HQ+UpASjJAFAOlGS4oGSlGBcvw0A0omSFA+UpATj+m0AkE6UpHigJCUY020AkE6UpHigJCUYJQkA0omSFA+UpARjTRIApBMlKR4oSQnGmiQASKfKytAJIFGSEo3pNgAAioeSlGCUJAAAioeSlGCsSQIAoHgoSQnGmiQAAIqHkpRgcZluMwudID74uwCA9MirJJnZnmb2vpnNMLOfFzsU8hOXkgQAQBo1WZLMrEzSlZJGSxosaayZDS52MDSNkpQ8S5Ys0fDhw7XFFltoyJAhOvfccyVJX331lUaNGqVBgwZp1KhRmjdv3n+/5+KLL9bAgQO18cYb67HHHvvv7VOnTtVmm22mgQMH6uSTT5ZzTpK0dOlSHXbYYRo4cKC23XZbffLJJ5FZGvt+AICXz0jScEkznHMfO+eWSZosaf/ixkI+2rcPnQDN1b59ez399NN66623NG3aNE2ZMkWvvPKKLrnkEu2222768MMPtdtuu+mSSy6RJL377ruaPHmy3nnnHU2ZMkXHH3+8ampqJEnHHXecJk6cqA8//FAffvihpkyZIkmaNGmS1l13Xc2YMUOnnXaa/u///i8yS2PfDwDw8ilJG0j6vMHHM+tuQwyw4ViymJlyuZwkafny5Vq+fLnMTA888IDGjRsnSRo3bpzuv/9+SdIDDzygww8/XO3bt9eAAQM0cOBAvfbaa6qqqtLChQu1/fbby8x09NFHr/I99fd18MEH66mnnlpjlGht3w8A8PIpSVFLUdcYlzez8Wb2upm9Pnfu3NYnQ17i8ldtxpHvou2amhoNHTpUPXr00KhRo7Tttttq9uzZ6l13HYLevXtrzpw5kqRZs2apb9++//3ePn36aNasWZo1a5b69Omzxu2rf095ebnWWWcdffnll6tkWNv3AwC88jy+Zqakvg0+7iPpP6t/kXNuoqSJkjRs2DAWN5RImzaKxVqSGESIhXyKUllZmaZNm6b58+frwAMP1PTp0xv92qh/WzNr9Pa1fU8+9wsgrDg8n2OlfEaS/iFpkJkNMLN2kg6X9GBxYwHp16VLF+2yyy6aMmWKevbsqaqqKkl+KqxHjx6S/AjP55+vnO2eOXOm1l9/ffXp00czZ85c4/bVv2fFihVasGCBunbtusqfvbbvBwB4TZYk59wKSSdKekzSe5LudM69U+xgQBrNnTtX8+fPlyQtXrxYTz75pDbZZBPtt99+uvHGGyVJN954o/bf358bsd9++2ny5MlaunSp/v3vf+vDDz/U8OHD1bt3b1VWVuqVV16Rc0433XTTKt9Tf1933323Ro4cucYo0dq+HwDg5TPdJufcI5IeKXIWIPWqqqo0btw41dTUqLa2Voceeqj22Wcfbb/99jr00EM1adIk9evXT3fddZckaciQITr00EM1ePBglZeX68orr1RZWZkk6S9/+Yt+9KMfafHixRo9erRGjx4tSTrmmGN01FFHaeDAgeratasmT5783z9/6NChmjZt2lq/HwDgWTHmP4cNG+Zef/31gt8vAABAoZnZVOfcsNVv57IkAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEcw5V/g7NZsr6dOC33F+ukn6ItCfHRKPO1t43NnC484WHnfpbeic6776jUUpSSGZ2evOuWGhc5QajztbeNzZwuPOFh53fDDdBgAAEIGSBAAAECGNJWli6ACB8LizhcedLTzubOFxx0Tq1iQBAAAUQhpHkgAAAFqNkgQAABAhdSXJzA4xs3fMrNbMYnUqYTGY2Z5m9r6ZzTCzn4fOUypmdp2ZzTGz6aGzlIqZ9TWzZ8zsvbr/46eEzlQKZtbBzF4zs7fqHvd5oTOVkpmVmdmbZvZw6CylYmafmNnbZjbNzF4PnadUzKyLmd1tZv+q+znfPnSmYjOzjev+neuPhWZ2auhc9VK3JsnMviepVtI1ks50zqX2B8zMyiR9IGmUpJmS/iFprHPu3aDBSsDMdpL0jaSbnHObhs5TCmbWW1Jv59wbZlYpaaqkA9L+721mJqnCOfeNmbWV9IKkU5xzrwSOVhJmdrqkYZI6O+f2CZ2nFMzsE0nDnHOZ2lDRzG6U9Lxz7lozayepk3NufuBYJVP3mjZL0rbOuVAbUq8idSNJzrn3nHPvh85RIsMlzXDOfeycWyZpsqT9A2cqCefcc5K+Cp2jlJxzVc65N+re/1rSe5I2CJuq+Jz3Td2HbeuOdP121wgz6yNpb0nXhs6C4jKzzpJ2kjRJkpxzy7JUkOrsJumjuBQkKYUlKWM2kPR5g49nKgMvmpDMrL+kLSW9GjhKSdRNOU2TNEfSE865TDxuSX+U9DP50fEscZIeN7OpZjY+dJgS+Y6kuZKur5tevdbMKkKHKrHDJd0eOkRDiSxJZvakmU2PODIxitKARdyWid+ws8zMcpLukXSqc25h6Dyl4Jyrcc4NldRH0nAzS/0Uq5ntI2mOc25q6CwBjHDObSVptKQT6qbX065c0laS/uKc21LSIklZWmfaTtJ+ku4KnaWh8tABWsI5t3voDDExU1LfBh/3kfSfQFlQAnVrcu6RdKtz7t7QeUrNOTffzJ6VtKektC/aHyFpPzPbS1IHSZ3N7Bbn3JGBcxWdc+4/dW/nmNl98ksLngubquhmSprZYJT0bmWoJMkX4jecc7NDB2kokSNJ+K9/SBpkZgPqWvjhkh4MnAlFUreAeZKk95xzl4XOUypm1t3MutS931HS7pL+FTRUCTjnfuGc6+Oc6y//s/10FgqSmVXUnZiguummHyj9hVjOuWpJn5vZxnU37SYp1SdlrGasYjbVJqWwJJnZgWY2U9L2kv5mZo+FzlQszrkVkk6U9Jj8It47nXPvhE1VGmZ2u6SXJW1sZjPN7JjQmUpghKSjJI1scLrsXqFDlUBvSc+Y2T/lfzF4wjmXmdPhM6inpBfM7C1Jr0n6m3NuSuBMpXKSpFvr/q8PlXRR2DilYWad5M/Sjt3oeOq2AAAAACiE1I0kAQAAFAIlCQAAIAIlCQAAIAIlCQAAIAIlCQAAIAIlCQAAIAIlCQAAIML/B+QmAT7HPkHuAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plotpar = [2, 1]\n", + "sfac = cfv.scalfact2(ex3, ey3, es3[:, 2], 0.2)\n", + "cfu.disp(f\"sfac={sfac}\")\n", + "\n", + "cfv.figure(4, fig_size=(10, 10))\n", + "cfv.secforce2(ex1, ey1, es1[:, 2], plotpar, sfac)\n", + "cfv.secforce2(ex2, ey2, es2[:, 2], plotpar, sfac)\n", + "cfv.secforce2(ex3, ey3, es3[:, 2], plotpar, sfac)\n", + "cfv.axis([-1.5, 7.5, -0.5, 5.5])\n", + "cfv.scalgraph2(sfac, [3e4, 0.5, 0], plotpar1)\n", + "cfv.title(\"Moment\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Draw moment diagram" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "sfac=3.628630851048567e-05" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkkAAAJOCAYAAACjhZOMAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAAsTAAALEwEAmpwYAAA6lUlEQVR4nO3dd3yV5f3/8feHhJkTRGQqILSgFhyoiAPrQKniFid10P6sfL/u2X7bilqts8MOR5WKe+CeVdzWrRXFiloVrQOaACpDkZlcvz+upAS4Q06Sc851j9fz8bgfSU6Sw/sAOeed67ru6zbnnAAAALCqNqEDAAAAxBElCQAAIAIlCQAAIAIlCQAAIAIlCQAAIAIlCQAAIAIlCQAAIAIlCUDBmNknZrbMzLqtdvs0M3Nm1j9QtDWY2Q1mdkHoHADii5IEoND+LWls/QdmtpmkjuHiAEDLUJIAFNrNko5u8PE4STfVf2Bm65jZTWY218w+NbMJZtam7nM/MrMXzewPZjbfzD42sx3qbv/czOaY2bgG99XezH5nZp+Z2Wwzu9rMOtZ9bhczm2lmZ9R9X5WZ/bjuc+MlHSHpZ2b2jZk9VIK/FwAJQ0kCUGivSOpsZt8zszJJh0m6pcHnL5e0jqTvSNpZvlD9uMHnt5X0T0nrSbpN0mRJ20gaKOlISVeYWa7uay+VtJGkoXWf30DSOQ3uq1fdn7WBpGMkXWlm6zrnJkq6VdJvnHM559y+hXnoANKEkgSgGOpHk0ZJ+pekWXW315emXzjnvnbOfSLp95KOavC9/3bOXe+cq5F0h6S+ks53zi11zj0uaZmkgWZmko6VdJpz7ivn3NeSLpJ0eIP7Wl73vcudc49I+kbSxsV5yADSpjx0AACpdLOk5yQNUIOpNkndJLWT9GmD2z6VH+mpN7vB+4slyTm3+m05Sd0ldZI01fclSZLJF7F6XzrnVjT4+Nu67wWAJjGSBKDgnHOfyi/g3kvSvQ0+9YX86M6GDW7rp5UjTc3xhXxhGuKc61J3rOOcy7cEuRb8mQAyhJIEoFiOkTTSObeowW01ku6UdKGZVZrZhpJO16prlvLinKuV9FdJfzCzHpJkZhuY2R553sVs+XVRABCJkgSgKJxzHznnXo/41EmSFkn6WNIL8ouzr2vhH/N/kmZIesXMFkp6UvmvOZokaXDdWXT3t/DPB5Bi5hwjzgAAAKtjJAkAACACJQkAACACJQkAACACJQkAACBCUTaT7Natm+vfv38x7hoAAKCgpk6d+oVzrvvqtxelJPXv31+vvx515i8AAEC8mNmnUbcz3QYAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABCBkgQAABChPJ8vMrNPJH0tqUbSCufcsGKGAgAACC2vklRnV+fcF0VLAgAAECNMtwEAAETItyQ5SY+b2VQzG1/MQAAAAHGQ73TbCOfcf8ysh6QnzOxfzrnnGn5BXXkaL0n9+vUrcEwAAIDSymskyTn3n7q3cyTdJ2l4xNdMdM4Nc84N6969e2FTAgAAlFiTJcnMKsyssv59ST+QNL3YwQAAAELKZ7qtp6T7zKz+629zzk0paioAAIDAmixJzrmPJW1RgiwAAACxwRYAAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAAAAEShJAADExIIFoROgIUoSAAAxMGuWtMkmklnoJKhHSQIAIAZOPlk69tjQKdAQJQkAgMDuv1+aPl365S9DJ0FD5aEDAACQZQsXSiedJN1yi9ShQ+g0aIiRJAAAAvrlL6U99pB23jl0EqyOkSQAAAJ5+WXp3nuld94JnQRRGEkCACCAZcuk8eOlP/xBWnfd0GkQhZIEAEAAv/ud1K+fdOihoZOgMUy3AQBQYh98IF12mTR1KvsixRkjSQAAlJBz0v/8jzRh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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plotpar = [2, 1]\n", + "sfac = cfv.scalfact2(ex3, ey3, es3[:, 2], 0.2)\n", + "cfu.disp(f\"sfac={sfac}\")\n", + "\n", + "cfv.figure(4, fig_size=(10, 10))\n", + "cfv.secforce2(ex1, ey1, es1[:, 2], plotpar, sfac)\n", + "cfv.secforce2(ex2, ey2, es2[:, 2], plotpar, sfac)\n", + "cfv.secforce2(ex3, ey3, es3[:, 2], plotpar, sfac)\n", + "cfv.axis([-1.5, 7.5, -0.5, 5.5])\n", + "cfv.scalgraph2(sfac, [3e4, 0.5, 0], plotpar1)\n", + "cfv.title(\"Moment\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/examples/exs_beam2.py b/examples/exs_beam2.py index ad6594c..0774ee5 100644 --- a/examples/exs_beam2.py +++ b/examples/exs_beam2.py @@ -23,22 +23,22 @@ # ----- Topology ------------------------------------------------- edof = np.array([ - [4, 5, 6, 1, 2, 3], - [7, 8, 9, 10, 11, 12], - [4, 5, 6, 7, 8, 9] + [4, 5, 6, 1, 2, 3], + [7, 8, 9, 10, 11, 12], + [4, 5, 6, 7, 8, 9] ]) # ----- Stiffness matrix K and load vector f --------------------- K = np.array(np.zeros((12, 12))) f = np.array(np.zeros((12, 1))) -f[3] = 2.e+3 +f[3] = 2.0e3 # ----- Element stiffness and element load matrices ------------- -E = 200.e9 -A1 = 2.e-3 -A2 = 6.e-3 +E = 200.0e9 +A1 = 2.0e-3 +A2 = 6.0e-3 I1 = 1.6e-5 I2 = 5.4e-5 @@ -52,7 +52,7 @@ ey3 = np.array([4, 4]) eq1 = np.array([0, 0]) eq2 = np.array([0, 0]) -eq3 = np.array([0, -10e+3]) +eq3 = np.array([0, -10e3]) Ke1 = cfc.beam2e(ex1, ey1, ep1) Ke2 = cfc.beam2e(ex2, ey2, ep1) @@ -60,19 +60,17 @@ # ----- Assemble Ke into K --------------------------------------- -cfc.assem(edof[0, :], K, Ke1) -cfc.assem(edof[1, :], K, Ke2) -cfc.assem(edof[2, :], K, Ke3, f, fe3) +K = cfc.assem(edof[0, :], K, Ke1) +K = cfc.assem(edof[1, :], K, Ke2) +K, f = cfc.assem(edof[2, :], K, Ke3, f, fe3) # ----- Solve the system of equations and compute reactions ------ bc = np.array([1, 2, 3, 10, 11]) a, r = cfc.solveq(K, f, bc) -print("a = ") -print(a) -print("r = ") -print(r) +cfu.disp_array(a, ["a"]) +cfu.disp_array(r, ["r"]) # ----- Section forces ------------------------------------------- @@ -82,18 +80,18 @@ es2, edi2, ec2 = cfc.beam2s(ex2, ey2, ep1, ed[1, :], eq2, nep=21) es3, edi3, ec3 = cfc.beam2s(ex3, ey3, ep3, ed[2, :], eq3, nep=21) -print("es1 = ") -print(es1) -print("edi1 = ") -print(edi1) -print("es2 = ") -print(es2) -print("edi2 = ") -print(edi2) -print("es3 = ") -print(es3) -print("edi3 = ") -print(edi3) +cfu.disp_h2("es1") +cfu.disp_array(es1, ["N", "Vy", "Mz"]) +cfu.disp_h2("edi1") +cfu.disp_array(edi1, ["u1", "v1"]) +cfu.disp_h2("es2") +cfu.disp_array(es2, ["N", "Vy", "Mz"]) +cfu.disp_h2("edi2") +cfu.disp_array(edi2, ["u1", "v1"]) +cfu.disp_h2("es3") +cfu.disp_array(es3, ["N", "Vy", "Mz"]) +cfu.disp_h2("edi3") +cfu.disp_array(edi3, ["u1", "v1"]) # ----- Draw deformed frame --------------------------------------- @@ -113,46 +111,46 @@ cfv.dispbeam2(ex3, ey3, edi3, plotpar, sfac) cfv.axis([-1.5, 7.5, -0.5, 5.5]) plotpar1 = 2 -cfv.scalgraph2(sfac,[1e-2, 0.5, 0],plotpar1); -cfv.title('Displacements') +cfv.scalgraph2(sfac, [1e-2, 0.5, 0], plotpar1) +cfv.title("Displacements") # ----- Draw normal force diagram -------------------------------- plotpar = [2, 1] -sfac = cfv.scalfact2(ex1, ey1, es1[:,0], 0.2) +sfac = cfv.scalfact2(ex1, ey1, es1[:, 0], 0.2) cfv.figure(2) -cfv.secforce2(ex1, ey1, es1[:,0], plotpar, sfac) -cfv.secforce2(ex2, ey2, es2[:,0], plotpar, sfac) -cfv.secforce2(ex3, ey3, es3[:,0], plotpar, sfac) +cfv.secforce2(ex1, ey1, es1[:, 0], plotpar, sfac) +cfv.secforce2(ex2, ey2, es2[:, 0], plotpar, sfac) +cfv.secforce2(ex3, ey3, es3[:, 0], plotpar, sfac) cfv.axis([-1.5, 7.5, -0.5, 5.5]) -cfv.scalgraph2(sfac,[3e4, 1.5, 0],plotpar1) -cfv.title('Normal force') +cfv.scalgraph2(sfac, [3e4, 1.5, 0], plotpar1) +cfv.title("Normal force") # ----- Draw shear force diagram --------------------------------- plotpar = [2, 1] -sfac = cfv.scalfact2(ex3, ey3, es3[:,1], 0.2) +sfac = cfv.scalfact2(ex3, ey3, es3[:, 1], 0.2) cfv.figure(3) -cfv.secforce2(ex1, ey1, es1[:,1], plotpar, sfac) -cfv.secforce2(ex2, ey2, es2[:,1], plotpar, sfac) -cfv.secforce2(ex3, ey3, es3[:,1], plotpar, sfac) +cfv.secforce2(ex1, ey1, es1[:, 1], plotpar, sfac) +cfv.secforce2(ex2, ey2, es2[:, 1], plotpar, sfac) +cfv.secforce2(ex3, ey3, es3[:, 1], plotpar, sfac) cfv.axis([-1.5, 7.5, -0.5, 5.5]) -cfv.scalgraph2(sfac,[3e4, 0.5, 0],plotpar1) -cfv.title('Shear force') +cfv.scalgraph2(sfac, [3e4, 0.5, 0], plotpar1) +cfv.title("Shear force") # ----- Draw moment diagram -------------------------------------- plotpar = [2, 1] -sfac = cfv.scalfact2(ex3, ey3, es3[:,2], 0.2) +sfac = cfv.scalfact2(ex3, ey3, es3[:, 2], 0.2) print("sfac=") print(sfac) cfv.figure(4) -cfv.secforce2(ex1, ey1, es1[:,2], plotpar, sfac) -cfv.secforce2(ex2, ey2, es2[:,2], plotpar, sfac) -cfv.secforce2(ex3, ey3, es3[:,2], plotpar, sfac) +cfv.secforce2(ex1, ey1, es1[:, 2], plotpar, sfac) +cfv.secforce2(ex2, ey2, es2[:, 2], plotpar, sfac) +cfv.secforce2(ex3, ey3, es3[:, 2], plotpar, sfac) cfv.axis([-1.5, 7.5, -0.5, 5.5]) -cfv.scalgraph2(sfac,[3e4, 0.5, 0],plotpar1) -cfv.title('Moment') +cfv.scalgraph2(sfac, [3e4, 0.5, 0], plotpar1) +cfv.title("Moment") -cfv.showAndWait() \ No newline at end of file +cfv.show_and_wait() diff --git a/examples/exs_beambar2.py b/examples/exs_beambar2.py index 70633c4..2dc1ef6 100644 --- a/examples/exs_beambar2.py +++ b/examples/exs_beambar2.py @@ -72,21 +72,22 @@ # ----- Assemble Ke into K --------------------------------------- -cfc.assem(edof1[0, :], K, Ke1) -cfc.assem(edof1[1, :], K, Ke2, f, fe2) -cfc.assem(edof1[2, :], K, Ke3, f, fe3) -cfc.assem(edof2[0, :], K, Ke4) -cfc.assem(edof2[1, :], K, Ke5) +K = cfc.assem(edof1[0, :], K, Ke1) +K, f = cfc.assem(edof1[1, :], K, Ke2, f, fe2) +K, f = cfc.assem(edof1[2, :], K, Ke3, f, fe3) +K = cfc.assem(edof2[0, :], K, Ke4) +K = cfc.assem(edof2[1, :], K, Ke5) # ----- Solve the system of equations and compute reactions ------ bc = np.array([1, 2, 3, 13, 14]) a, r = cfc.solveq(K, f, bc) -print("a = ") -print(a) -print("r = ") -print(r) +cfu.disp_h2("Displacements a:") +cfu.disp_array(a) + +cfu.disp_h2("Reaction forces r:") +cfu.disp_array(r) # ----- Section forces ------------------------------------------- @@ -99,14 +100,14 @@ es4 = cfc.bar2s(ex4, ey4, ep4, ed2[0, :]) es5 = cfc.bar2s(ex5, ey5, ep4, ed2[1, :]) -print("es1 = ") -print(es1) -print("es2 = ") -print(es2) -print("es3 = ") -print(es3) -print("es4 = ") -print(es4) -print("es5 = ") -print(es5) +cfu.disp_h2("es1 = ") +cfu.disp_array(es1, headers=["N", "Q", "M"]) +cfu.disp_h2("es2 = ") +cfu.disp_array(es2, headers=["N", "Q", "M"]) +cfu.disp_h2("es3 = ") +cfu.disp_array(es3, headers=["N", "Q", "M"]) +cfu.disp_h2("es4 = ") +cfu.disp_array(es4, headers=["N"]) +cfu.disp_h2("es5 = ") +cfu.disp_array(es5, headers=["N"]) diff --git a/examples/exs_flw_diff2.py b/examples/exs_flw_diff2.py index 7943209..570b988 100644 --- a/examples/exs_flw_diff2.py +++ b/examples/exs_flw_diff2.py @@ -1,107 +1,103 @@ # -*- coding: utf-8 -*- # example exs8 -#---------------------------------------------------------------- -# PURPOSE +# ---------------------------------------------------------------- +# PURPOSE # Analysis of two dimensional diffusion -#---------------------------------------------------------------- +# ---------------------------------------------------------------- # REFERENCES # Karl-Gunnar Olsson 1995-10-08 # Ola Dahlblom 2004-09-14 -#---------------------------------------------------------------- +# ---------------------------------------------------------------- import numpy as np import calfem.vis_mpl as cfv import calfem.core as cfc +import calfem.utils as cfu # ----- System matrices ----- -K = np.zeros((15,15)) -f = np.zeros((15,1)) -Coord = np.array([ - [0, 0 ],[0.025, 0 ], - [0.05, 0 ],[0, 0.025], - [0.025, 0.025],[0.05, 0.025], - [0, 0.05 ],[0.025, 0.05 ], - [0.05, 0.05 ],[0, 0.075], - [0.025, 0.075],[0.05, 0.075], - [0, 0.1 ],[0.025, 0.1 ], - [0.05, 0.1 ] -]) - -Dof = np.array([ - [1 ],[2 ],[3 ], - [4 ],[5 ],[6 ], - [7 ],[8 ],[9 ], - [10],[11],[12], - [13],[14],[15] -]) +K = np.zeros((15, 15)) +f = np.zeros((15, 1)) +Coord = np.array( + [ + [0, 0], + [0.025, 0], + [0.05, 0], + [0, 0.025], + [0.025, 0.025], + [0.05, 0.025], + [0, 0.05], + [0.025, 0.05], + [0.05, 0.05], + [0, 0.075], + [0.025, 0.075], + [0.05, 0.075], + [0, 0.1], + [0.025, 0.1], + [0.05, 0.1], + ] +) + +Dof = np.array( + [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12], [13], [14], [15]] +) # ----- Element properties, topology and coordinates ----- ep = np.array([1]) -D = np.array([ - [1, 0], - [0, 1] -]) -Edof = np.array([ - [ 1, 2, 5, 4], - [ 2, 3, 6, 5], - [ 4, 5, 8, 7], - [ 5, 6, 9, 8], - [ 7, 8,11,10], - [ 8, 9,12,11], - [10,11,14,13], - [11,12,15,14], -]) -Ex,Ey = cfc.coordxtr(Edof,Coord,Dof) +D = np.array([[1, 0], [0, 1]]) +Edof = np.array( + [ + [1, 2, 5, 4], + [2, 3, 6, 5], + [4, 5, 8, 7], + [5, 6, 9, 8], + [7, 8, 11, 10], + [8, 9, 12, 11], + [10, 11, 14, 13], + [11, 12, 15, 14], + ] +) +Ex, Ey = cfc.coordxtr(Edof, Coord, Dof) # ----- Generate FE-mesh ----- -#clf; eldraw2(Ex,Ey,[1 3 0],Edof(:,1)); -#disp('PRESS ENTER TO CONTINUE'); pause; clf; +# clf; eldraw2(Ex,Ey,[1 3 0],Edof(:,1)); +# disp('PRESS ENTER TO CONTINUE'); pause; clf; # ----- Create and assemble element matrices ----- for i in range(8): - Ke = cfc.flw2qe(Ex[i],Ey[i],ep,D) - K = cfc.assem(Edof[i],K,Ke) + Ke = cfc.flw2qe(Ex[i], Ey[i], ep, D) + K = cfc.assem(Edof[i], K, Ke) # ----- Solve equation system ----- -bcPrescr = np.array([1,2,3,4,7,10,13,14,15]) -bcVal = np.array([0,0,0,0,0,0,0.5e-3,1e-3,1e-3]) -a,r = cfc.solveq(K,f,bcPrescr,bcVal) +bcPrescr = np.array([1, 2, 3, 4, 7, 10, 13, 14, 15]) +bcVal = np.array([0, 0, 0, 0, 0, 0, 0.5e-3, 1e-3, 1e-3]) +a, r = cfc.solveq(K, f, bcPrescr, bcVal) # ----- Compute element flux vector ----- -Ed = cfc.extractEldisp(Edof,a) -Es = np.zeros((8,2)) +Ed = cfc.extractEldisp(Edof, a) +Es = np.zeros((8, 2)) for i in range(8): - Es[i],Et = cfc.flw2qs(Ex[i],Ey[i],ep,D,Ed[i]) + Es[i], Et = cfc.flw2qs(Ex[i], Ey[i], ep, D, Ed[i]) -# ----- Draw flux vectors and contourlines ----- - -print(Ex) -print(Ey) -print(a) -print(Ed) - -cfv.eldraw2(Ex, Ey, [1, 2, 1], range(1,Ex.shape[0]+1)) -cfv.eliso2_mpl(Ex,Ey,Ed) -cfv.showAndWaitMpl() -#cfv.showAndWait() -#sfac=scalfact2(Ex,Ey,Es,0.5); -#eldraw2(Ex,Ey); -#elflux2(Ex,Ey,Es,[1,4],sfac); -#pltscalb2(sfac,[2e-2 0.06 0.01],4); -#disp('PRESS ENTER TO CONTINUE'); pause; clf; -#eldraw2(Ex,Ey,[1,3,0]); -#eliso2(Ex,Ey,Ed,5,[1,4]); -#hold off; -#echo off; - -# ----------------- End -------------------------------- +# ----- Draw flux vectors and contourlines ----- +cfu.disp_h2("Ex") +cfu.disp_array(Ex, headers=["x0", "x1", "x2", "x3"]) +cfu.disp_h2("Ey") +cfu.disp_array(Ey, headers=["x0", "x1", "x2", "x3"]) +cfu.disp_h2("a") +cfu.disp_array(a) +cfu.disp_h2("Ed") +cfu.disp_array(Ed, headers=["ed0", "ed1", "ed2", "ed3"]) + +cfv.eldraw2(Ex, Ey, [1, 2, 1], range(1, Ex.shape[0] + 1)) +cfv.eliso2_mpl(Ex, Ey, Ed) +cfv.show_and_wait() diff --git a/examples/exs_flw_temp1.py b/examples/exs_flw_temp1.py index b92c2e1..83010f8 100644 --- a/examples/exs_flw_temp1.py +++ b/examples/exs_flw_temp1.py @@ -15,6 +15,7 @@ import numpy as np import calfem.core as cfc +import calfem.utils as cfu # ----- Topology ------------------------------------------------- @@ -60,10 +61,11 @@ bcVal = np.array([-17, 20]) a, r = cfc.solveq(K, f, bc, bcVal) -print("a = ") -print(a) -print("r = ") -print(r) +cfu.disp_h2("Temperatures a:") +cfu.disp_array(a) + +cfu.disp_h2("Reaction flows r:") +cfu.disp_array(r) # ----- Section forces ------------------------------------------- @@ -79,6 +81,8 @@ q4 = cfc.spring1s(ep4, ed4) q5 = cfc.spring1s(ep5, ed5) +cfu.disp_h2("Element flows:") + print("q1 = ") print(q1) print("q2 = ") diff --git a/examples/exs_flw_temp2.py b/examples/exs_flw_temp2.py index 5d64d5e..05caa70 100644 --- a/examples/exs_flw_temp2.py +++ b/examples/exs_flw_temp2.py @@ -15,6 +15,7 @@ import numpy as np import calfem.core as cfc +import calfem.utils as cfu # ----- Topology matrix Edof ------------------------------------- @@ -56,8 +57,8 @@ cfc.assem(edof[3, :], K, Ke4) cfc.assem(edof[4, :], K, Ke5) -print("Stiffness matrix K:") -print(K) +cfu.disp_h2("Stiffness matrix K:") +cfu.disp_array(K) # ----- Solve the system of equations ---------------------------- @@ -65,11 +66,11 @@ bcVal = np.array([-17.0, 20.0]) a, r = cfc.solveq(K, f, bc, bcVal) -print("Displacements a:") -print(a) +cfu.disp_h2("Temperatures a:") +cfu.disp_array(a) -print("Reaction forces r:") -print(r) +cfu.disp_h2("Reaction flows r:") +cfu.disp_array(r) # ----- Element flows ------------------------------------------- @@ -85,6 +86,8 @@ q4 = cfc.spring1s(ep4, ed4) q5 = cfc.spring1s(ep5, ed5) +cfu.disp_h2("Element flows r:") + print("q1 = "+str(q1)) print("q2 = "+str(q2)) print("q3 = "+str(q3)) diff --git a/examples/exs_spring.py b/examples/exs_spring.py index 7fa38ff..b79ba22 100644 --- a/examples/exs_spring.py +++ b/examples/exs_spring.py @@ -18,6 +18,7 @@ import numpy as np import calfem.core as cfc +import calfem.utils as cfu # ----- Topology matrix Edof @@ -46,8 +47,8 @@ cfc.assem(edof[1, :], K, Ke1) cfc.assem(edof[2, :], K, Ke2) -print("Stiffness matrix K:") -print(K) +cfu.disp_h2("Stiffness matrix K:") +cfu.disp_array(K) # f[1] corresponds to edof 2 @@ -58,11 +59,11 @@ bc = np.array([1, 3]) a, r = cfc.solveq(K, f, bc) -print("Displacements a:") -print(a) +cfu.disp_h2("Displacements a:") +cfu.disp_array(a) -print("Reaction forces Q:") -print(r) +cfu.disp_h2("Reaction forces r:") +cfu.disp_array(r) # ----- Caculate element forces @@ -74,7 +75,7 @@ es2 = cfc.spring1s(ep1, ed2) es3 = cfc.spring1s(ep2, ed3) -print("Element forces N:") +cfu.disp_h2("Element forces N:") print("N1 = "+str(es1)) print("N2 = "+str(es2)) print("N3 = "+str(es3)) diff --git a/test_calfem.py b/test_calfem.py index 4ed255c..9fe84a0 100644 --- a/test_calfem.py +++ b/test_calfem.py @@ -1,321 +1,71 @@ # -*- coding: utf-8 -*- - -import unittest, sys, io - -from subprocess import Popen, PIPE - -def exec_process(source_filename): - p = Popen(['python', source_filename], stdin=PIPE, stdout=PIPE) - s_out, _ = p.communicate() - - return s_out.decode() - - -#print('Here is what the script produced:\n\n', s_out.decode()) - -exs1_expected_output = """Stiffness matrix K: -[[ 3000. -3000. 0.] - [-3000. 7500. -4500.] - [ 0. -4500. 4500.]] -Displacements a: -[[0. ] - [0.01333333] - [0. ]] -Reaction forces Q: -[[-40.] - [ 0.] - [-60.]] -Element forces N: -N1 = 40.0 -N2 = -20.0 -N3 = -40.0 -""" - -exs2_expected_output = """Stiffness matrix K: -[[ 25. -25. 0. 0. 0. 0. ] - [-25. 49.3 -24.3 0. 0. 0. ] - [ 0. -24.3 24.7 -0.4 0. 0. ] - [ 0. 0. -0.4 17.4 -17. 0. ] - [ 0. 0. 0. -17. 24.7 -7.7] - [ 0. 0. 0. 0. -7.7 7.7]] -Displacements a: -[[-17. ] - [-16.43842455] - [-15.86067203] - [ 19.23779344] - [ 19.47540439] - [ 20. ]] -Reaction forces r: -[[-1.40393862e+01] - [ 0.00000000e+00] - [ 0.00000000e+00] - [ 0.00000000e+00] - [ 5.68434189e-14] - [ 4.03938619e+00]] -q1 = 14.039386189223357 -q2 = 14.039386189223451 -q3 = 14.039386189223485 -q4 = 4.039386189223492 -q5 = 4.03938618922342 -""" - -exs3_expected_output = """Stiffness matrix K: -[[ 7.50e+07 0.00e+00 0.00e+00 0.00e+00 -7.50e+07 0.00e+00 0.00e+00 - 0.00e+00] - [ 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 - 0.00e+00] - [ 0.00e+00 0.00e+00 6.40e+07 -4.80e+07 -6.40e+07 4.80e+07 0.00e+00 - 0.00e+00] - [ 0.00e+00 0.00e+00 -4.80e+07 3.60e+07 4.80e+07 -3.60e+07 0.00e+00 - 0.00e+00] - [-7.50e+07 0.00e+00 -6.40e+07 4.80e+07 1.39e+08 -4.80e+07 0.00e+00 - 0.00e+00] - [ 0.00e+00 0.00e+00 4.80e+07 -3.60e+07 -4.80e+07 8.60e+07 0.00e+00 - -5.00e+07] - [ 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 - 0.00e+00] - [ 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 -5.00e+07 0.00e+00 - 5.00e+07]] -Displacements a: -[[ 0. ] - [ 0. ] - [ 0. ] - [ 0. ] - [-0.00039793] - [-0.00115233] - [ 0. ] - [ 0. ]] -Reaction forces r: -[[ 29844.55958549] - [ 0. ] - [-29844.55958549] - [ 22383.41968912] - [ 0. ] - [ 0. ] - [ 0. ] - [ 57616.58031088]] -N1 = -29844.559585492225 -N2 = 57616.580310880825 -N3 = 37305.69948186528 """ +This is very simple test of the calfem package. -exs4_expected_output = """Stiffness matrix K: -[[ 3.55307765e+08 -9.28077650e+07 0.00000000e+00 0.00000000e+00 - -2.62500000e+08 0.00000000e+00 -9.28077650e+07 9.28077650e+07 - 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00] - [-9.28077650e+07 9.28077650e+07 0.00000000e+00 0.00000000e+00 - 0.00000000e+00 0.00000000e+00 9.28077650e+07 -9.28077650e+07 - 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00] - [ 0.00000000e+00 0.00000000e+00 3.55307765e+08 9.28077650e+07 - -9.28077650e+07 -9.28077650e+07 -2.62500000e+08 0.00000000e+00 - 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00] - [ 0.00000000e+00 0.00000000e+00 9.28077650e+07 9.28077650e+07 - -9.28077650e+07 -9.28077650e+07 0.00000000e+00 0.00000000e+00 - 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00] - [-2.62500000e+08 0.00000000e+00 -9.28077650e+07 -9.28077650e+07 - 7.10615530e+08 0.00000000e+00 0.00000000e+00 0.00000000e+00 - -2.62500000e+08 0.00000000e+00 -9.28077650e+07 9.28077650e+07] - [ 0.00000000e+00 0.00000000e+00 -9.28077650e+07 -9.28077650e+07 - 0.00000000e+00 4.48115530e+08 0.00000000e+00 -2.62500000e+08 - 0.00000000e+00 0.00000000e+00 9.28077650e+07 -9.28077650e+07] - [-9.28077650e+07 9.28077650e+07 -2.62500000e+08 0.00000000e+00 - 0.00000000e+00 0.00000000e+00 7.10615530e+08 0.00000000e+00 - -9.28077650e+07 -9.28077650e+07 -2.62500000e+08 0.00000000e+00] - [ 9.28077650e+07 -9.28077650e+07 0.00000000e+00 0.00000000e+00 - 0.00000000e+00 -2.62500000e+08 0.00000000e+00 4.48115530e+08 - -9.28077650e+07 -9.28077650e+07 0.00000000e+00 0.00000000e+00] - [ 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 - -2.62500000e+08 0.00000000e+00 -9.28077650e+07 -9.28077650e+07 - 3.55307765e+08 9.28077650e+07 0.00000000e+00 0.00000000e+00] - [ 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 - 0.00000000e+00 0.00000000e+00 -9.28077650e+07 -9.28077650e+07 - 9.28077650e+07 3.55307765e+08 0.00000000e+00 -2.62500000e+08] - [ 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 - -9.28077650e+07 9.28077650e+07 -2.62500000e+08 0.00000000e+00 - 0.00000000e+00 0.00000000e+00 3.55307765e+08 -9.28077650e+07] - [ 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 - 9.28077650e+07 -9.28077650e+07 0.00000000e+00 0.00000000e+00 - 0.00000000e+00 -2.62500000e+08 -9.28077650e+07 3.55307765e+08]] -Displacements a: -[[ 0. ] - [ 0. ] - [ 0. ] - [ 0. ] - [ 0.00238453] - [-0.0044633 ] - [-0.00161181] - [-0.00419874] - [ 0.00303458] - [-0.01068377] - [-0.00165894] - [-0.01133382]] -Reaction forces r: -[[-8.66025404e+05] - [ 2.40086918e+05] - [ 6.16025404e+05] - [ 1.92925784e+05] - [ 2.32830644e-10] - [-2.32830644e-10] - [ 1.16415322e-10] - [-1.16415322e-10] - [-2.32830644e-10] - [ 3.49245965e-10] - [-2.91038305e-11] - [ 4.65661287e-10]] -Element forces: -N1 = 625938 -N2 = -423100 -N3 = 170640 -N4 = -12372.8 -N5 = -69447 -N6 = 170640 -N7 = -272838 -N8 = -241321 -N9 = 339534 -N10 = 371051 +Make sure all examples run without errors. """ -exs5_expected_output = """[[ 3.17100000e+08 0.00000000e+00 0.00000000e+00 -3.17100000e+08 - 0.00000000e+00 0.00000000e+00] - [ 0.00000000e+00 2.34266667e+06 3.51400000e+06 0.00000000e+00 - -2.34266667e+06 3.51400000e+06] - [ 0.00000000e+00 3.51400000e+06 7.02800000e+06 0.00000000e+00 - -3.51400000e+06 3.51400000e+06] - [-3.17100000e+08 0.00000000e+00 0.00000000e+00 3.17100000e+08 - 0.00000000e+00 0.00000000e+00] - [ 0.00000000e+00 -2.34266667e+06 -3.51400000e+06 0.00000000e+00 - 2.34266667e+06 -3.51400000e+06] - [ 0.00000000e+00 3.51400000e+06 3.51400000e+06 0.00000000e+00 - -3.51400000e+06 7.02800000e+06]] -a= -[[ 0. ] - [ 0. ] - [-0.00948587] - [ 0. ] - [-0.02276608] - [-0.00379435] - [ 0. ] - [-0.01992032] - [ 0.00474293] - [ 0. ] - [ 0. ] - [ 0.00758869]] -r= -[[0.00000000e+00] - [6.66666667e+03] - [3.63797881e-12] - [0.00000000e+00] - [7.27595761e-12] - [3.63797881e-12] - [0.00000000e+00] - [0.00000000e+00] - [3.63797881e-12] - [0.00000000e+00] - [3.33333333e+03] - [7.27595761e-12]] -es1= -[[ 0.00000000e+00 -6.66666667e+03 9.14372744e-12] - [ 0.00000000e+00 -6.66666667e+03 2.22222222e+03] - [ 0.00000000e+00 -6.66666667e+03 4.44444444e+03] - [ 0.00000000e+00 -6.66666667e+03 6.66666667e+03] - [ 0.00000000e+00 -6.66666667e+03 8.88888889e+03] - [ 0.00000000e+00 -6.66666667e+03 1.11111111e+04] - [ 0.00000000e+00 -6.66666667e+03 1.33333333e+04] - [ 0.00000000e+00 -6.66666667e+03 1.55555556e+04] - [ 0.00000000e+00 -6.66666667e+03 1.77777778e+04] - [ 0.00000000e+00 -6.66666667e+03 2.00000000e+04] - [ 0.00000000e+00 -6.66666667e+03 2.22222222e+04]] -es2= -[[ 0. 3333.33333333 20000. ] - [ 0. 3333.33333333 18888.88888889] - [ 0. 3333.33333333 17777.77777778] - [ 0. 3333.33333333 16666.66666667] - [ 0. 3333.33333333 15555.55555556] - [ 0. 3333.33333333 14444.44444444] - [ 0. 3333.33333333 13333.33333333] - [ 0. 3333.33333333 12222.22222222] - [ 0. 3333.33333333 11111.11111111] - [ 0. 3333.33333333 10000. ] - [ 0. 3333.33333333 8888.88888889]] -es3= -[[ 0.00000000e+00 3.33333333e+03 1.00000000e+04] - [ 0.00000000e+00 3.33333333e+03 8.88888889e+03] - [ 0.00000000e+00 3.33333333e+03 7.77777778e+03] - [ 0.00000000e+00 3.33333333e+03 6.66666667e+03] - [ 0.00000000e+00 3.33333333e+03 5.55555556e+03] - [ 0.00000000e+00 3.33333333e+03 4.44444444e+03] - [ 0.00000000e+00 3.33333333e+03 3.33333333e+03] - [ 0.00000000e+00 3.33333333e+03 2.22222222e+03] - [ 0.00000000e+00 3.33333333e+03 1.11111111e+03] - [ 0.00000000e+00 3.33333333e+03 2.17163527e-11] - [ 0.00000000e+00 3.33333333e+03 -1.11111111e+03]] -ed1= -[[ 0. 0. ] - [ 0. -0.00315415] - [ 0. -0.00626145] - [ 0. -0.00927507] - [ 0. -0.01214815] - [ 0. -0.01483386] - [ 0. -0.01728536] - [ 0. -0.01945578] - [ 0. -0.02129831] - [ 0. -0.02276608] - [ 0. -0.02381226]] -ed2= -[[ 0. -0.02276608] - [ 0. -0.02382397] - [ 0. -0.02448368] - [ 0. -0.02476865] - [ 0. -0.02470229] - [ 0. -0.02430802] - [ 0. -0.02360927] - [ 0. -0.02262945] - [ 0. -0.02139199] - [ 0. -0.01992032] - [ 0. -0.01823785]] -ed3= -[[ 0.00000000e+00 -1.99203187e-02] - [ 0.00000000e+00 -1.82378462e-02] - [ 0.00000000e+00 -1.63679985e-02] - [ 0.00000000e+00 -1.43341976e-02] - [ 0.00000000e+00 -1.21598653e-02] - [ 0.00000000e+00 -9.86842362e-03] - [ 0.00000000e+00 -7.48329434e-03] - [ 0.00000000e+00 -5.02789938e-03] - [ 0.00000000e+00 -2.52566063e-03] - [ 0.00000000e+00 1.73472348e-18] - [ 0.00000000e+00 2.52566063e-03]] - """ - -class TestCalfemCore(unittest.TestCase): - - def run_test(self, filename, expected_output): - saved_stdout = sys.stdout - try: - out = io.StringIO() - sys.stdout = out - output = exec_process(filename) - - cleaned_output = output.replace("\r", "") - - self.assertEqual(cleaned_output.strip(), expected_output.strip(), "Output from %s differs from expected." % (filename)) - finally: - sys.stdout = saved_stdout - - def test_exs1(self): - self.run_test('examples/exs1.py', exs1_expected_output) - - def test_exs2(self): - self.run_test('examples/exs2.py', exs2_expected_output) - - def test_exs3(self): - self.run_test('examples/exs3.py', exs3_expected_output) - - def test_exs4(self): - self.run_test('examples/exs4a.py', exs4_expected_output) - - def test_exs5(self): - self.run_test('examples/exs5.py', exs5_expected_output) - -if __name__ == '__main__': - unittest.main() \ No newline at end of file +import os + + +def test_examples(): + + examples_dir = "./examples" + + examples = [ + "exd_beam2_b.py", + "exd_beam2_m.py", + "exd_beam2_t.py", + "exd_beam2_tr.py", + "exm_circle_bsplines.py", + "exm_flow_model.py", + "exm_geometry.py", + "exm_stress_2d.py", + "exm_stress_2d_export.py", + "exm_stress_2d_materials.py", + "exm_stress_2d_pyvtk.py", + "exm_structured_mesh.py", + "exm_temp_2d_markers.py", + "exm_temp_2d_splines_arcs.py", + "exm_tutorial_1.py", + "exm_tutorial_2.py", + "exn_bar2g.py", + "exn_bar2m.py", + "exn_beam2.py", + "exn_beam2_b.py", + "exs_bar2.py", + "exs_bar2_la.py", + "exs_bar2_lb.py", + "exs_beam1.py", + "exs_beam2.py", + "exs_beambar2.py", + "exs_flw_diff2.py", + "exs_flw_temp1.py", + "exs_flw_temp2.py", + "exs_spring.py" + ] + + if os.path.exists("test_examples.log"): + os.remove("test_examples.log") + + os.environ["CFV_NO_BLOCK"] = "YES" + + return_codes = 0 + + for example in examples: + print(f"Running: {example}", end="") + + echo_string = f"echo ------ {example} " + os.system(echo_string + "-"*(40-len(example)) + + " >> test_examples.log 2>&1") + + example_path = os.path.join(examples_dir, example) + + return_code = os.system( + f"python {example_path} >> test_examples.log 2>&1") + + return_codes += return_code + + print(f" return_code = {return_code}") + + assert return_codes == 0