From 364901cc22e47740beae39ee80c3c15be38c4c2b Mon Sep 17 00:00:00 2001 From: Andreas Dutzler Date: Wed, 15 Mar 2023 22:49:26 +0100 Subject: [PATCH 01/13] Update model.py --- src/trusspy/model.py | 38 ++++++++------------------------------ 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/src/trusspy/model.py b/src/trusspy/model.py index 30ed9b7..ae2e448 100644 --- a/src/trusspy/model.py +++ b/src/trusspy/model.py @@ -6,7 +6,6 @@ """ import copy -import os import sys import time from subprocess import run as sp_run @@ -137,14 +136,11 @@ def __init__(self, file=None, log=2, logfile=False, logfile_name="analysis"): self.Boundaries = BoundaryHandler() self.ExtForces = ExternalForceHandler() self.Settings = SettingsHandler() - # self.Results = ResultHandler() - # self.Analysis = Analysis() self.Settings.log = log if log > 1: print(" - finished.\n") - # if log > 1: print('-'*88+'\n') if file is not None: if log > 1: @@ -196,10 +192,9 @@ def __init__(self, file=None, log=2, logfile=False, logfile_name="analysis"): if log > 1: print(" - Import finished.\n") - # if log > 1: print('-'*88) def build(self): - """build Model (r,U,K,...) with Model data and dimensions.""" + "Build Model (r,U,K,...) with Model data and dimensions." self.Results = ResultHandler() self.Analysis = Analysis() @@ -228,8 +223,7 @@ def build(self): if 2 in self.Elements.mat_type: self.Settings.nstatev = 2 - # node properties - # n----pro------- + # (n)ode (pro)perties # # active = free = 1 # inactive = fixed = 0 @@ -374,10 +368,6 @@ def run(self): self.Analysis.Vred = np.append(self.Analysis.Ured, 0) self.Analysis.lpf = 0 - # init LPF - # self.Settings.lpf = self.Settings.dlpf - # self.Analysis.lpf = self.Settings.dlpf - for step in range(self.Settings.nsteps): # maximum number of increment and maximum value per step @@ -401,9 +391,6 @@ def run(self): print(r"$$\text{Value}_i = \left|\frac{D_x}{D_{x,max}}\right|_i$$") - # get reduced external force vector - # f0red = self.ExtForces.forces[:,3*(step):3*(step+1)].flatten()[self.Analysis.DOF1] - # self.Analysis.f0red = f0red.reshape(len(f0red),1) self.Analysis.ExtForces = copy.deepcopy(self.ExtForces) f0_const = np.zeros_like( @@ -544,15 +531,11 @@ def run(self): ) def stiffness(self, Ured, analysis=None): - "Method for Stiffness Matrix." - - # In a future version this function should be implemented in Stiffness class part of the Model or Results - # self.Stiffness(reduced=True) - - # it re-shapes stiffness matrix to - # K(nnodes,nnodes,ndim,ndim) --> K(nnodes*ndim,nnodes*nim) - # and returns a view on the reduced (active part of the) matrix - # K(nnodes*ndim,nnodes*nim)[active DOF rows][:,active DOF columns] + """Method for evaluating the stiffness matrix. It re-shapes the stiffness matrix + to ``K(nnodes,nnodes,ndim,ndim) --> K(nnodes*ndim,nnodes*nim)`` and returns a + view on the reduced (active part of the) matrix + ``K(nnodes*ndim,nnodes*nim)[active DOF rows][:,active DOF columns]``. + """ if analysis is not None: self.Analysis = analysis @@ -577,7 +560,7 @@ def stiffness(self, Ured, analysis=None): return self.Analysis.Kred def equilibrium(self, Ured, U0red, stage="G", analysis=None, statev_write=False): - """Method to generate equilibrium for given displacements and external forces.""" + "Method to generate equilibrium for given displacements and external forces." if analysis is not None: self.Analysis = analysis @@ -585,7 +568,6 @@ def equilibrium(self, Ured, U0red, stage="G", analysis=None, statev_write=False) # remove last entry in Vred to get only displacement DOFs if len(Ured) > self.ndof1: lpf = Ured[-1] - lpf0 = U0red[-1] Ured = Ured[: self.ndof1] U0red = U0red[: self.ndof1] else: @@ -642,14 +624,11 @@ def equilibrium(self, Ured, U0red, stage="G", analysis=None, statev_write=False) rnodes[n] = self.Analysis.r[np.where(self.Nodes.labels == node)][0] mat_type = self.Elements.get_material_type(e) - # elem_type = self.Elements.get_element_type(e) if mat_type == 1: umat = umat_el elif mat_type == 2: umat = umat_elplast_iso - elif mat_type == 3: - umat = umat_elplast_kiniso self.Analysis, state_v = truss( e, @@ -668,7 +647,6 @@ def equilibrium(self, Ured, U0red, stage="G", analysis=None, statev_write=False) self.Analysis, ) if statev_write and self.Settings.nstatev > 0: - # print('write state-variable for element', int(e), state_v[0]) self.Analysis.state_v = state_v self.Analysis.lpf = lpf self.Analysis.Vred = np.append(Ured, lpf) From 72f3f0cd8c5000cef2b6fe7cd3e29a3664f5d60c Mon Sep 17 00:00:00 2001 From: Andreas Dutzler Date: Wed, 15 Mar 2023 22:49:28 +0100 Subject: [PATCH 02/13] Update tpsolver.py --- src/trusspy/solvers/tpsolver.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/trusspy/solvers/tpsolver.py b/src/trusspy/solvers/tpsolver.py index 15e8211..4b5922d 100644 --- a/src/trusspy/solvers/tpsolver.py +++ b/src/trusspy/solvers/tpsolver.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- """ -Created on Mon Jul 30 17:36:41 2018 - -@author: adutz +title: TrussPy - Truss Solver for Python +author: Andreas Dutzler +year: 2023 """ import copy From edf50c515edb134baf51df0c341c99f2057a72d1 Mon Sep 17 00:00:00 2001 From: Andreas Dutzler Date: Wed, 15 Mar 2023 22:53:52 +0100 Subject: [PATCH 03/13] shorten printed header in `Model` --- src/trusspy/model.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/trusspy/model.py b/src/trusspy/model.py index ae2e448..aff5791 100644 --- a/src/trusspy/model.py +++ b/src/trusspy/model.py @@ -117,10 +117,7 @@ def __init__(self, file=None, log=2, logfile=False, logfile_name="analysis"): TrussPy - Truss Solver for Python Version {__version__} -Author: Dutzler A. - Graz University of Technology, 2018 - -TrussPy Copyright (C) 2023 Andreas Dutzler +Dutzler Andreas, Graz University of Technology, 2023 """ ) From e0b3dfb5067d2831dd4a69018c2a901f017bf251 Mon Sep 17 00:00:00 2001 From: Andreas Dutzler Date: Wed, 15 Mar 2023 22:53:56 +0100 Subject: [PATCH 04/13] Delete result.py --- src/trusspy/core/result.py | 124 ------------------------------------- 1 file changed, 124 deletions(-) delete mode 100644 src/trusspy/core/result.py diff --git a/src/trusspy/core/result.py b/src/trusspy/core/result.py deleted file mode 100644 index 0468c02..0000000 --- a/src/trusspy/core/result.py +++ /dev/null @@ -1,124 +0,0 @@ -# -*- coding: utf-8 -*- -""" -title: TrussPy - Truss Solver for Python -author: Andreas Dutzler -year: 2023 -""" - -import numpy as np - - -class Result: - """Result class. - - Attributes - ---------- - U : ndarray - 2d-array with total displacement vector - `U.shape = (nnodes, ndim)`. To get Ux,Uy,Uz of node i type: `U[i]` - - r : ndarray - 2d-array with internal force vector - `r.shape = (nnodes, ndim)`. To get rx,ry,rz of node i type: `r[i]` - - g : ndarray - 2d-array with equilibrium vector - `g.shape = (nnodes, ndim)`. To get gx,gy,gz of node i type: `g[i]` - - stretch : ndarray - 2d-array with stretch vector - `stretch.shape = (nelems, 1)`. To get stretch of element i - type: `stretch[i]` - - element_force : ndarray - 2d-array with element force vector - `element_force.shape = (nelems, 1)`. To get element_force of element i - type: `element_force[i]` - - K : ndarray - 4d-array with stiffness matrix - `K.shape = (nnodes,nnodes,ndim,ndim)`. To get K with shape (ndim,ndim) - between node i and node j type K[i,j]. - - state_v : ndarray - state variable vector - - - - Todo - ---- - * simplify reduced set with DOF1 read/write options - """ - - def __init__(self): - self.U = None - self.r = None - self.g = None - self.stretch = None - self.element_force = None - self.K = None - self.state_v = None - - def build(self, nnodes, nelems, ndim, DOF0, DOF1, nstatev=0): - """Build/initialize result class with given dimensions. - - Parameter - --------- - nnodes : int - Number of nodes - nelems : int - Number of elements - ndim : int - Dimension of analysis - DOF0 : int - indices of inactive degree of freedoms - (generated with flattened degree of freedoms) - DOF1 : int - indices of active degree of freedoms - (generated with flattened degree of freedoms) - - Raises - ---------- - ValueError : Result is not empty and can't be built. - Dimensions already set. - - Todo - ---- - * simplify reduced slice read/write operations - """ - if self.U is None: - self.U = np.zeros((nnodes, ndim)) - self.U0 = np.zeros((nnodes, ndim)) - self.r = np.zeros((nnodes, ndim)) - self.g = np.zeros((nnodes, ndim)) - self.K = np.zeros((nnodes, nnodes, ndim, ndim)) - # self.dlamdU = np.zeros((nnodes,ndim)) - self.stretch = np.zeros((nelems, 1)) - self.element_force = np.zeros((nelems, 1)) - self.element_stress0 = np.zeros((nelems, 1)) - self.element_stress = np.zeros((nelems, 1)) - self.DOF0 = DOF0 - self.DOF1 = DOF1 - self.Ured = self.get_U(flat=True)[DOF1] - self.rred = self.get_r(flat=True)[DOF1] - if nstatev > 0: - self.state_v = np.zeros((nelems, nstatev)) - - else: - raise ValueError("Displacements or Residuals already loaded.") - - def get_U(self, flat=False, red=False): - if flat: - return self.U.flatten() - elif red: - return self.Ured - else: - return self.U - - def get_r(self, flat=False, red=False): - if flat: - return self.r.flatten() - elif red: - return self.rred - else: - return self.r From 908a881df1ad77bf35def3d7fb1cf401db20b0d4 Mon Sep 17 00:00:00 2001 From: Andreas Dutzler Date: Wed, 15 Mar 2023 22:55:48 +0100 Subject: [PATCH 05/13] Update analysis.py --- src/trusspy/core/analysis.py | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/src/trusspy/core/analysis.py b/src/trusspy/core/analysis.py index 49f8be8..949bd66 100644 --- a/src/trusspy/core/analysis.py +++ b/src/trusspy/core/analysis.py @@ -16,64 +16,48 @@ class Analysis: U : None placeholder for 2d-array with total displacement vector at the end of the inc. `U.shape = (nnodes, ndim)`. To get Ux,Uy,Uz of node i type: `U[i]` - U0 : None - placeholder for 2d-array with total displacement vector at the beginning of the inc. - `U0.shape = (nnodes, ndim)`. To get U0x,U0y,U0z of node i type: `U0[i]` - + placeholder for 2d-array with total displacement vector at the beginning of the + inc. `U0.shape = (nnodes, ndim)`. To get U0x,U0y,U0z of node i type: `U0[i]` r : None placeholder for 2d-array with internal force vector `r.shape = (nnodes, ndim)`. To get rx,ry,rz of node i type: `r[i]` - g : None placeholder for 2d-array with equilibrium vector `g.shape = (nnodes, ndim)`. To get gx,gy,gz of node i type: `g[i]` - K : None placeholder for 4d-array with stiffness matrix `K.shape = (nnodes,nnodes,ndim,ndim)`. To get K with shape (ndim,ndim) between force of node i w.r.t. displacement of node j type K[i,j]. - stretch : None placeholder for 2d-array with stretch vector `stretch.shape = (nelems, 1)`. To get stretch of element i type: `stretch[i]` - element_force : None placeholder for 2d-array with element force vector `element_force.shape = (nelems, 1)`. To get element_force of element i type: `element_force[i]` - element_stress : None placeholder for 2d-array with element stress vector `element_stress.shape = (nelems, 1)`. To get element_stress of element i type: `element_stress[i]` - element_stress0 : None - placeholder for 2d-array with element stress vector at the beginning of the increment - `element_stress0.shape = (nelems, 1)`. To get element_stress0 of element i - type: `element_stress0[i]` - + placeholder for 2d-array with element stress vector at the beginning of the + increment ``element_stress0.shape = (nelems, 1)``. To get element_stress0 of + element ``i`` type: ``element_stress0[i]``. DOF0 : None placeholder for indices of inactive (locked) degree of freedoms - DOF1 : None placeholder for indices of active (free) degree of freedoms - Ured : None placeholder for reduced dispaclement vector (only active components of U) - Vred : None placeholder for reduced dispaclement vector (only active components of V) - state_v : None placeholder for state variable vector - Todo - ---- - * simplify reduced set with DOF1 read/write options """ def __init__(self): @@ -122,9 +106,6 @@ def build(self, nnodes, nelems, ndim, DOF0, DOF1, nstatev=0): ValueError : Result is not empty and can't be built. Dimensions already set. - Todo - ---- - * simplify reduced slice read/write operations """ if self.U is None: self.step = 1 From 985f8244d3ca07d3be6f381afc4db6391cd878d6 Mon Sep 17 00:00:00 2001 From: Andreas Dutzler Date: Wed, 15 Mar 2023 23:01:32 +0100 Subject: [PATCH 06/13] simplify core components --- src/trusspy/core/boundary.py | 3 -- src/trusspy/core/element.py | 52 ++++++++++++------------------ src/trusspy/core/external_force.py | 25 +++++++------- src/trusspy/core/node.py | 24 +++++++------- 4 files changed, 46 insertions(+), 58 deletions(-) diff --git a/src/trusspy/core/boundary.py b/src/trusspy/core/boundary.py index ea288c3..356d0e8 100644 --- a/src/trusspy/core/boundary.py +++ b/src/trusspy/core/boundary.py @@ -27,9 +27,6 @@ class BoundaryU: Array of boundary components: `array([U1, U2, U3])`. Set 1 for active (free) DOF and 0 for inactive (locked) DOF. - Todo - ---- - - rename to DOFState or something similar """ def __init__(self, node, values): diff --git a/src/trusspy/core/element.py b/src/trusspy/core/element.py index aa028d6..37f144b 100644 --- a/src/trusspy/core/element.py +++ b/src/trusspy/core/element.py @@ -11,24 +11,6 @@ class Element: """Element class. - Parameter - --------- - label : int - Element ID number - conn : array_like - List of Element connectivity : `[Node 1, Node 2]` - - elem_type : int, optional - Element Type. (default is 1 for truss) - mat_type : int, optional - Material Type. (default is 1 for linear-elastic) - material_properties : array_like, optional - List with material parameters, - e.g. [Young's modulus, Thermal expansion coefficient] (default is []) - geometric_properties : array_like, optional - List with geometric parameters, - e.g. [Section area] (default is []) - Attributes ---------- label : int @@ -46,11 +28,6 @@ class Element: List with geometric parameters, e.g. [Section area] (default is []) - Todo - ---- - + DONE rewrite material parameter storage - + DONE material properties: type, mechanical and thermal parameter - + DONE geometric porperties: section area """ def __init__( @@ -64,6 +41,27 @@ def __init__( mprop=None, gprop=None, ): + """Initialize an instance of the Element class. + + Parameters + ---------- + label : int + Element ID number + conn : array_like + List of Element connectivity ``[Node 1, Node 2]``. + elem_type : int, optional + Element Type (default is 1 for "truss"). + mat_type : int, optional + Material Type (default is 1 for "linear-elastic"). + material_properties : array_like, optional + List with material parameters, + e.g. ``[Young's modulus, Thermal expansion coefficient]`` (default is + [np.nan]). + geometric_properties : array_like, optional + List with geometric parameters, + e.g. ``[Section area]`` (default is [np.nan]). + + """ self.label = label self.conn = np.array(conn) @@ -89,11 +87,3 @@ def __init__( self.geometric_properties = gprop else: self.geometric_properties = geometric_properties - - # def get_NE(self): - # """get end node of element""" - # return self.conn[1] - - # def get_NA(self): - # """get begin node of element""" - # return self.conn[0] diff --git a/src/trusspy/core/external_force.py b/src/trusspy/core/external_force.py index 7b1a8a9..28c16ee 100644 --- a/src/trusspy/core/external_force.py +++ b/src/trusspy/core/external_force.py @@ -11,25 +11,26 @@ class ExternalForce: """External Force class. - Parameter - --------- - node : int - Node ID number - force : array_like - List of Force components: `[F1, F2, F3]` - Attributes ---------- node : int - Node ID number + The node ID number, force : ndarray - Array containing Force components `array([F1, F2, F3])` + Array containing force components ``array([F1, F2, F3])``. - TODO's - ------ - - add steps/stages by extending `force` """ def __init__(self, node, force): + """External Force class. + + Parameters + ---------- + node : int + The node ID number. + force : array_like + List of Force components ``[F1, F2, F3]``. + + """ + self.node = node self.force = np.array(force, dtype=float) diff --git a/src/trusspy/core/node.py b/src/trusspy/core/node.py index e0b3113..e875769 100644 --- a/src/trusspy/core/node.py +++ b/src/trusspy/core/node.py @@ -11,26 +11,26 @@ class Node: """Node class. - Parameter - --------- - label : int - Node ID number - coord : array_like - List of node coordinates: `[X, Y, Z]` - Attributes ---------- label : int Node ID number coord : ndarray - List of node coordinates: `[X, Y, Z]` + List of node coordinates: ``[X, Y, Z]`` - Todo - ---- - - add undeformed/deformed coordinates (useful?) - - copy deformed coordinates to result class at the end of each increment """ def __init__(self, label, coord): + """Node class. + + Parameters + ---------- + label : int + Node ID number + coord : array_like + List of node coordinates: ``[X, Y, Z]`` + + """ + self.label = label self.coord = np.array(coord) From 9ac7a35cdc4423444a72f9789d07c09e98335a47 Mon Sep 17 00:00:00 2001 From: Andreas Dutzler Date: Wed, 15 Mar 2023 23:05:10 +0100 Subject: [PATCH 07/13] simplify handlers --- src/trusspy/elements/element_definition.py | 1 - src/trusspy/handlers/handler_boundary.py | 5 ----- src/trusspy/handlers/handler_element.py | 13 ------------- 3 files changed, 19 deletions(-) diff --git a/src/trusspy/elements/element_definition.py b/src/trusspy/elements/element_definition.py index da017bb..5996401 100644 --- a/src/trusspy/elements/element_definition.py +++ b/src/trusspy/elements/element_definition.py @@ -39,7 +39,6 @@ def truss( dx0 = dX + dU0 # elemental normal vector in deformed configuration - n0 = dx / np.linalg.norm(dx0) n = dx / np.linalg.norm(dx) # elemental length in undeformed and deformed configuration diff --git a/src/trusspy/handlers/handler_boundary.py b/src/trusspy/handlers/handler_boundary.py index c7195a9..ffe6d5e 100644 --- a/src/trusspy/handlers/handler_boundary.py +++ b/src/trusspy/handlers/handler_boundary.py @@ -94,8 +94,3 @@ def add_bounds_T(self, BB): # add list of thermal boundaries for B in BB: self.add_bound_T(B) - - # def DOFactive(self,ndof): - # # 0 = gesperrt - # O = np.ones(ndof) - # np.where(self.Uvalues, 0 or False) diff --git a/src/trusspy/handlers/handler_element.py b/src/trusspy/handlers/handler_element.py index 913de7f..03431db 100644 --- a/src/trusspy/handlers/handler_element.py +++ b/src/trusspy/handlers/handler_element.py @@ -121,19 +121,6 @@ def add_element_matrix(self, EM, MM, GM, TM): self.conns = np.array(EM[:, 2:4]) self.material_properties = np.vstack((MM[:, 2:12].T, TM[:, 1])).T self.geometric_properties = GM[:, 1].reshape(len(GM), 1) - else: - pass - # self.labels = np.append(self.labels,EM[:,0]) - # self.elem_type = np.append(self.elem_type, np.array(EM[:,1])) - # self.mat_type = np.append( self.mat_type, np.array(EM[:,2])) - # self.conns = np.vstack((self.conns,EM[:,3:5])) - # self.material = np.append(self.material,EM[:,3]) - # self.area = np.append(self.area,EM[:,4]) - # self.alpha = np.append(self.alpha,TM[:,1]) - # self.material_properties = np.vstack((self.material_properties, - # np.vstack((EM[:,5], TM[:,1])).T)) - # self.geometric_properties = np.append(self.geometric_properties, - # EM[:,6].reshape(len(EM),1)) def get_nodes(self, label): "choose element label and return connected end node" From 6d0da12b80459492cc4891236a02a42ed9fb0e79 Mon Sep 17 00:00:00 2001 From: Andreas Dutzler Date: Wed, 15 Mar 2023 23:06:31 +0100 Subject: [PATCH 08/13] Update material_definition.py --- src/trusspy/materials/material_definition.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/trusspy/materials/material_definition.py b/src/trusspy/materials/material_definition.py index 4b975f4..346e629 100644 --- a/src/trusspy/materials/material_definition.py +++ b/src/trusspy/materials/material_definition.py @@ -20,17 +20,15 @@ def umat_el(e, de, mat_prop, state_v): def umat_elplast_iso(e, de, mat_prop, state_v): + E = mat_prop[0] K = mat_prop[1] Sy = mat_prop[2] - # eps_el_0 = stress/E eps_pl_0 = state_v[1] - # eps_el_0 = e-de-eps_pl_0 - # eps_0 = eps_el_0 + eps_pl_0 # total strain at time t=t_n+1 - eps_1 = e # eps_0+de + eps_1 = e # yield stress at time t=t_n if state_v[0] == 0: @@ -43,27 +41,20 @@ def umat_elplast_iso(e, de, mat_prop, state_v): # trial state stress_trial = E * (eps_1 - eps_pl_0) eps_pl_trial = eps_pl_0 - # alpha_0 = (stateolde(1)-1)/kmod; - # alpha_trial = alpha_0; f_trial = abs(stress_trial) - stress_yield_0 if f_trial <= 0: - # print('elastic step') # elastic strain increment stress_1 = stress_trial - eps_pl_1 = eps_pl_trial # eps_pl_1 = eps_pl_0 - # alpha_1 = alpha_trial; % alpha_1 = alpha_0 + eps_pl_1 = eps_pl_trial dsde = E - # elastic tangent delta_gamma = 0.0 else: - # print('plastic step') # plastic strain increment delta_gamma = f_trial / (E + K) stress_1 = (1 - (delta_gamma * E) / abs(stress_trial)) * stress_trial eps_pl_1 = eps_pl_0 + delta_gamma * np.sign(stress_trial) - # alpha_1 = alpha_0 + delta_gamma - dsde = E * K / (E + K) # elastoplastic tangent + dsde = E * K / (E + K) # output (update) stress s = stress_1 From e7b1c09b44259d334b8eca84f23213e1f4e75fd9 Mon Sep 17 00:00:00 2001 From: Andreas Dutzler Date: Wed, 15 Mar 2023 23:13:37 +0100 Subject: [PATCH 09/13] simplify tools and solvers --- src/trusspy/solvers/tpsolver.py | 11 ++---- src/trusspy/solvers/zerosearch.py | 23 ++----------- src/trusspy/tools/helper_functions2.py | 30 +++-------------- src/trusspy/tools/plot_utilities.py | 46 ++------------------------ 4 files changed, 11 insertions(+), 99 deletions(-) diff --git a/src/trusspy/solvers/tpsolver.py b/src/trusspy/solvers/tpsolver.py index 4b5922d..3ca94bf 100644 --- a/src/trusspy/solvers/tpsolver.py +++ b/src/trusspy/solvers/tpsolver.py @@ -79,8 +79,8 @@ def dxmax_control( given value `decrease`. Both an increase and decrease is only performed inside the interval `minfac*dxmax0 <= dxmax <= maxfac*dxmax0`. - Parameter - --------- + Parameters + ---------- dxmax : ndarray current incremental limit of system vector x dxmax0 : ndarray @@ -165,7 +165,6 @@ def dxmax_control( if j0 == j: if z > 0: pass - # if verbose > 1: print('* increase NR-step size by factor: (inactive due to recycling).'.format(b,3)) elif b <= 3: if verbose > 1: print( @@ -230,10 +229,8 @@ def pathfollow( 2D Derivative of function w.r.t. `x` x : ndarray 1D Initial solution vector - analysis : trusspy.core.analysis Analysis object - dxmax: array_like, optional Maximum incremental step in Dx. Several input methods are possible. A single float value will assign dxmax to all components of Dx. @@ -267,7 +264,6 @@ def pathfollow( Tolerance for residual of function: `norm(f)` (default is 1e-8) xtol : float, optional Tolerance for residual of `x`: `norm(x)` (default is 1e-8) - stepcontrol : bool, optional Automatic adjustment of incremental stepwidth Dx. Parameters `macfac`, `minfac`, `reduce` and `increase` will control the adjustment. All @@ -285,12 +281,10 @@ def pathfollow( newton iterations to achieve convergence will be used. Updating dxmax follows `dxmax = dxmax*(1+(nfev-n)/nfev * increase)`. (default is 1) - dxtol : float, optional Allowed vvershoot factor of control component if solution has converged. Will help to speed-up solution and avoid unneccessary re- cycles. (default is 1.25) - verbose : int, optional Level of information during iterations (default is 0): `verbose=0` ... no information, @@ -303,7 +297,6 @@ def pathfollow( `res_x.shape = (incs, len(x)+1)`. Result array `res_x[1]` contains the converged solution vector for the first increment. Result component res_x[0] contains start solution which is typically zeros. - res_a: Analysis Analysis Class (trusspy.core.analysis.Analysis) with converged solution. """ diff --git a/src/trusspy/solvers/zerosearch.py b/src/trusspy/solvers/zerosearch.py index 523da2b..5deb5c3 100644 --- a/src/trusspy/solvers/zerosearch.py +++ b/src/trusspy/solvers/zerosearch.py @@ -5,15 +5,10 @@ year: 2023 """ -import copy - -import numpy as np -from numpy.linalg import norm, solve -from scipy.sparse import csc_matrix, csr_matrix +from numpy.linalg import norm +from scipy.sparse import csr_matrix from scipy.sparse.linalg import spsolve -# from numba import jit - def newton(f, dfdx, x, nfev=8, ftol=1e-8, xtol=1e-8, verbose=0, *args): """Find the roots of a function using the Newton-Rhapson algorithm. @@ -94,18 +89,7 @@ def newton(f, dfdx, x, nfev=8, ftol=1e-8, xtol=1e-8, verbose=0, *args): # NEWTON-RHAPSON ITERATIONS for n in range(nfev): - # dx = -np.linalg.inv(dfdx(x,j,xmax))@f(x,j,xmax) - - # DIRECT SPARSE SOLVER with cs"r" "R"ow optimized sparse matrix - # print('x', x) - # print('xmax', args[2]) - # print('f', f(x,*args)) - # print('dfdx', dfdx(x,*args)) dx = spsolve(csr_matrix(dfdx(x, *args)), -f(x, *args)) - # print('dx', dx[:3]) - - # DENSE SOLVER - # dx = solve(dfdx(x,*args),-f(x,*args)) # UPDATE SOLUTION x = x + dx @@ -115,14 +99,11 @@ def newton(f, dfdx, x, nfev=8, ftol=1e-8, xtol=1e-8, verbose=0, *args): x_norm = norm(dx) if verbose > 0: - # print(' {0:2d} |{1:1.3e} {2:1.3e}| | | |'.format( - # n,f_norm,x_norm)) print( "| | {0:2d} | |{1:1.3e}| | | | | | |".format( n + 1, f_norm ) ) - # print('+---+------+-------+---------+-----------+-----------+-----------+-----------+') if f_norm < ftol and x_norm < xtol: success = True diff --git a/src/trusspy/tools/helper_functions2.py b/src/trusspy/tools/helper_functions2.py index 4acaa3e..da16241 100644 --- a/src/trusspy/tools/helper_functions2.py +++ b/src/trusspy/tools/helper_functions2.py @@ -11,11 +11,6 @@ from matplotlib.patches import FancyArrowPatch from mpl_toolkits.mplot3d import proj3d -# from mpl_toolkits.mplot3d import Axes3D -# from numpy.linalg import norm - -# projection : [‘aitoff’ | ‘hammer’ | ‘lambert’ | ‘mollweide’ | ‘polar’ | ‘rectilinear’], optional - class Arrow3D(FancyArrowPatch): def __init__(self, xs, ys, zs, *args, **kwargs): @@ -54,7 +49,6 @@ def plot_hist(x, y, nl, xl, yl="LPF", fig=None, ax=None): ax.set_ylabel(yl) ax.legend() plt.gcf().set_size_inches(8, 6) - # plt.gca().set_aspect('equal') return fig, ax @@ -79,7 +73,6 @@ def plot_pth(x, y, inc, yl="LPF", fig=None, ax=None): ax.set_xticklabels(xtckl) ax.legend() plt.gcf().set_size_inches(8, 6) - # plt.gca().set_aspect('equal') return fig, ax @@ -92,19 +85,17 @@ def plot_nodes(X, fig=None, ax=None, view="xz", color="k", size=10): i, j = 1, 2 if view == "3d": - from mpl_toolkits.mplot3d import Axes3D if fig is None: fig = plt.figure() if ax is None: ax = fig.add_subplot(111, projection="3d") - # ax.scatter(X[:,0],X[:,1],X[:,2],marker='o',s=150,color=color,zorder=30) + ax.plot(X[:, 0], X[:, 1], X[:, 2], color + "o", ms=size, zorder=30) else: plt.scatter(X[:, i], X[:, j], marker="o", s=15 * size, color=color, zorder=30) fig = plt.gcf() ax = plt.gca() - # plt.axes().set_aspect('equal'); return fig, ax @@ -119,26 +110,23 @@ def plot_force(f0, X, fig=None, ax=None, view="xz", color="C2", scale=0.5): if view == "3d": i, j, k = 0, 1, 2 for f0i, Xi in zip(f0, X): - # print((f0i==np.zeros(3)).all()) if ~(f0i == np.zeros(3)).all(): xx = Xi[i] yy = Xi[j] if view == "3d": zz = Xi[k] if f0i[i] < 0: - # xx += -a*f0i[i] pass if f0i[j] < 0: - # yy += -a*f0i[j] pass - # plt.arrow(xx,yy,a*f0i[i],a*f0i[j],color=color,linewidth=4,zorder=3) + if view == "3d": - from mpl_toolkits.mplot3d import Axes3D if fig is None: fig = plt.figure() if ax is None: ax = fig.add_subplot(111, projection="3d") + arrow_properties = dict( mutation_scale=20, lw=3, @@ -153,7 +141,6 @@ def plot_force(f0, X, fig=None, ax=None, view="xz", color="C2", scale=0.5): [xx, xx], [yy, yy], [zz, zz + scale * f0i[k]], - # color=color,linewidth=3,zorder=20) alpha=0, ) a = Arrow3D( @@ -169,7 +156,6 @@ def plot_force(f0, X, fig=None, ax=None, view="xz", color="C2", scale=0.5): [xx, xx], [yy, yy + scale * f0i[j]], [zz, zz], - # color=color,linewidth=3,zorder=20) alpha=0, ) a = Arrow3D( @@ -185,7 +171,6 @@ def plot_force(f0, X, fig=None, ax=None, view="xz", color="C2", scale=0.5): [xx, xx + scale * f0i[i]], [yy, yy], [zz, zz], - # color=color,linewidth=3,zorder=20) alpha=0, ) a = Arrow3D( @@ -224,7 +209,7 @@ def plot_force(f0, X, fig=None, ax=None, view="xz", color="C2", scale=0.5): length_includes_head=True, ) plt.plot([xx, xx + 0], [yy, yy + scale * f0i[j]], alpha=0) - # plt.axes().set_aspect('equal'); + return fig, ax @@ -259,14 +244,12 @@ def plot_elems( else: norm = mpl.colors.Normalize(vmin=contour_lim[0], vmax=contour_lim[1]) color = colormap(norm(contour[k])) - # print(norm(contour[k]),contour[k]) # create a ScalarMappable and initialize a data structure s_m = mpl.cm.ScalarMappable(cmap=colormap, norm=norm) s_m.set_array([]) if view == "3d": - from mpl_toolkits.mplot3d import Axes3D if fig is None: fig = plt.figure() @@ -314,7 +297,6 @@ def plot_elems( ax=plt.gca(), ) plt.ylabel("CONTOUR = " + clabel) - # plt.gca().yaxis.set_label_position("right") try: if len(lim_scale) == 2: @@ -336,7 +318,6 @@ def plot_elems( ax = plot_coord(ax, m) zz = np.zeros_like(xx) ax.plot_surface(xx, yy, zz, alpha=0.2) - # print(ax.elev,ax.azim) ax.view_init(20, -40) except: @@ -355,8 +336,6 @@ def plot_elems( mlimx = (plt.xlim()[1] + plt.xlim()[0]) / 2 mlimy = (plt.ylim()[1] + plt.ylim()[0]) / 2 alim = lim_scale * max(alimx, alimy) - # lmin, lmax = (lim_scale*min(plt.xlim()[0],plt.ylim()[0]), - # lim_scale*max(plt.xlim()[1],plt.ylim()[1])) minx, maxx = (mlimx - alim, mlimx + alim) miny, maxy = (mlimy - alim, mlimy + alim) if abs(lim_scale) < 100: @@ -375,7 +354,6 @@ def plot_elems( ax = plot_coord(ax, 1) zz = np.zeros_like(xx) ax.plot_surface(xx, yy, zz, alpha=0.2) - # print(ax.elev,ax.azim) ax.view_init(20, -40) else: plt.gca().set_aspect("equal") diff --git a/src/trusspy/tools/plot_utilities.py b/src/trusspy/tools/plot_utilities.py index f80db8c..74081cf 100644 --- a/src/trusspy/tools/plot_utilities.py +++ b/src/trusspy/tools/plot_utilities.py @@ -160,16 +160,6 @@ def p_model( view=view, scale=force_scale, ) - # if 'both' in config: - # fig,ax = plot_nodes(self.Nodes.coords,color='C7',view=view) - # fig,ax = plot_elems(self.Elements.conns,self.Nodes.coords,fig,ax,color='C7',view=view) - # fig,ax = plot_nodes(self.Nodes.coords+self.Results.R[inc].U,fig,ax,color='C2',view=view) - # fig,ax = plot_elems(self.Elements.conns,self.Nodes.coords+self.Results.R[inc].U, - # fig,ax,color='C0',view=view,contour=con,lim_scale=lim_scale) - # fig,ax = plot_force(self.Results.R[inc].lpf*self.ExtForces.forces[:,3*(step-1):3*step], - # self.Nodes.coords+self.Results.R[inc].U, - # fig,ax,view=view, - # scale=force_scale) if force_scale is not None and ("deformed" in config or "undeformed" in config): # these are matplotlib.patch.Patch properties @@ -213,8 +203,6 @@ def p_model( plt.xlabel(view[0]) plt.ylabel(view[1]) - # plot_force(self.ExtForces.forces,self.Nodes.coords) - # plt.show() return fig, ax @@ -248,38 +236,12 @@ def p_movie( for i in incs: - # if contour == 'stretch': - # if cbar_limits == 'auto': - # contour_lim = [1-max(abs(self.Results.R[i].stretch[:,0]-1)),1+max(abs(self.Results.R[i].stretch[:,0]-1))] - # else: - # contour_lim = cbar_limits - # con = contour, self.Results.R[i].stretch[:,0], contour_lim - # if contour == 'force': - # if cbar_limits == 'auto': - # contour_lim = [-max(abs(self.Results.R[i].element_force[:,0])),max(abs(self.Results.R[i].element_force[:,0]))] - # else: - # contour_lim = cbar_limits - # con = contour, self.Results.R[i].element_force[:,0], contour_lim - # if contour == 'stress': - # if cbar_limits == 'auto': - # contour_lim = [-max(abs(self.Results.R[i].element_stress[:,0])),max(abs(self.Results.R[i].element_stress[:,0]))] - # else: - # contour_lim = cbar_limits - # con = contour, self.Results.R[i].element_stress[:,0], contour_lim - # if type(contour)==tuple: - # if contour[0] == 'stretch': - # con_data = self.Results.R[i].stretch[:,0] - # elif contour[0] == 'force': - # con_data = self.Results.R[i].element_force[:,0] - # elif contour[0] == 'stress': - # con_data = self.Results.R[i].element_stress[:,0] - # con = contour[0], con_data, contour[1] - self.plot_model( config, view, contour, lim_scale, force_scale, nodesize, cbar_limits, i ) plt.savefig("figures/png/fig_{:03d}.png".format(i), dpi=200) plt.close("all") + png_to_gif() @@ -309,8 +271,7 @@ def p_path(self, nodepath, increment=-1, Y="Displacement X", fig=None, ax=None): if fig is None: fig, ax = plt.subplots() plot_pth(nodepath, yy, increment, Y, fig, ax) - # print(np.array(xx),np.array(yy)) - # plt.show() + return fig, ax @@ -354,8 +315,7 @@ def p_history( if fig is None: fig, ax = plt.subplots() plot_hist(xx, yy, nodes[0], X, Y, fig, ax) - # print(np.array(xx),np.array(yy)) - # plt.show() + return fig, ax From 3ca3fd6ae0be51fcdca0a7b32b44f89c3ce087b9 Mon Sep 17 00:00:00 2001 From: Andreas Dutzler Date: Wed, 15 Mar 2023 23:20:31 +0100 Subject: [PATCH 10/13] Update model.py --- src/trusspy/model.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/trusspy/model.py b/src/trusspy/model.py index aff5791..857d171 100644 --- a/src/trusspy/model.py +++ b/src/trusspy/model.py @@ -103,6 +103,10 @@ def __init__(self, file=None, log=2, logfile=False, logfile_name="analysis"): sys.stdout = open(self.logfile_name + ".md", "w") if log > 1: + + if self.logfile: + print("```") + print( f""" _____ ______ @@ -120,6 +124,8 @@ def __init__(self, file=None, log=2, logfile=False, logfile_name="analysis"): Dutzler Andreas, Graz University of Technology, 2023 """ ) + if self.logfile: + print("```") if log > 1: print("") @@ -504,17 +510,17 @@ def run(self): if self.logfile: sys.stdout = self.stdout - sp_run( - [ - "pandoc", - self.logfile_name + ".md", - "-t", - "latex", - "-o", - self.logfile_name + ".pdf", - ] - ) if self.Settings.logpdf: + sp_run( + [ + "pandoc", + self.logfile_name + ".md", + "-t", + "latex", + "-o", + self.logfile_name + ".pdf", + ] + ) sp_run( [ "pandoc", From 1c87558a42a1f169c0808b5e445dae5970693197 Mon Sep 17 00:00:00 2001 From: Andreas Dutzler Date: Wed, 15 Mar 2023 23:24:32 +0100 Subject: [PATCH 11/13] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9db10a..5f0eafc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. The format ## [Unreleased] +### Changed +- Simplify the source code: Remove unused lines of code, To-Do's from function or class docstrings, unify file docstrings. +- Build HTML and PDF logfiles only if `Settings.logpdf=True` (Pandoc and LaTeX must be installed). Otherwise, only the Markdown logfile `analysis.md` for `Model(logfile=True)` is created (no Pandoc install required). + ## [1.0.2] - 2023-03-13 ### Added From 8120f75ba7827231be83a6c8268b3f7b7edcadfc Mon Sep 17 00:00:00 2001 From: Andreas Dutzler Date: Wed, 15 Mar 2023 23:26:37 +0100 Subject: [PATCH 12/13] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f0eafc..857e2c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,10 @@ All notable changes to this project will be documented in this file. The format ## [Unreleased] +## [1.0.3] - 2023-03-15 + ### Changed -- Simplify the source code: Remove unused lines of code, To-Do's from function or class docstrings, unify file docstrings. +- Simplify the source code: Remove unused lines of code, To-Do's from function or class docstrings, unify file docstrings. Trim the header printed by `Model`. - Build HTML and PDF logfiles only if `Settings.logpdf=True` (Pandoc and LaTeX must be installed). Otherwise, only the Markdown logfile `analysis.md` for `Model(logfile=True)` is created (no Pandoc install required). ## [1.0.2] - 2023-03-13 From c8c6030cb6548d9e1063521ee8d9a472abd661e7 Mon Sep 17 00:00:00 2001 From: Andreas Dutzler Date: Wed, 15 Mar 2023 23:26:40 +0100 Subject: [PATCH 13/13] Update __about__.py --- src/trusspy/__about__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/trusspy/__about__.py b/src/trusspy/__about__.py index 7863915..976498a 100644 --- a/src/trusspy/__about__.py +++ b/src/trusspy/__about__.py @@ -1 +1 @@ -__version__ = "1.0.2" +__version__ = "1.0.3"