Skip to content

Commit

Permalink
Add nonlinear shell elements and solver (#193)
Browse files Browse the repository at this point in the history
* Define some additional nonlinear element types

* Add cantilever example/benchmark

* Add cantilever example mesh

* Add more nonlinear example cases

* Add option to specify that problem is nonlinear

This is a temporary implementation until I can figure out how to figure it out automatically from the element/contitutive objects.

* add a `getForces` method

* fix capitalisation of `isNonlinear`

* black format

* more black formatting

* Revert "more black formatting"

This reverts commit 03676ce.

* More black formatting

* Nonlinear solver functioning with basic incrementation

* Remove skin_buckle benchmark

* Fix bug `addDirectorJacobian` in `TACSQuadraticRotation`

* Add moderate rotation elements to shell tests

* Improve clarity of printout for element mat derivative tests

* Add SolverHistory to utils

* Add methods for retreiving number of iterations and residual norm from KSM solvers

* black formatting

* Nonlinear solver working with solver history

* Example tweaks

* Add methods for retreiving number of iterations and residual norm from KSM solvers

* PCG fix

* clang-format

* Test `getResidualNorm` and `getIterCount` in `StaticTestCase`

* PCG fix

* Fix SuiteSparse installation commands

* Fix solution file writing

* Fix compiler warning

* Implemented adaptive linear solver convergence using Eisenstat-Walker method

* Add __init__ method to BaseUI

* Examples options tweaks

* More example options tweaks

* Add __init__ method to BaseUI

* Modify problem classes to use __init__ from BaseUI

* Modify problem classes to use __init__ from BaseUI

* add back isNonlinear to transient problem

* black formatting

* Fixing some imports

* Adding new standalone solver classes

* Add solvers docs pages

* Solvers __init__

* Starting to incorporate NewtonSolver insto Static problem, not working yet

* Address Graeme's comments

* Treat resNorm consistently in all solvers

* typo

* Remove unnecessary abs

* Make resNorm and iterCount getters virtual

* Fix bug in nonlinear solver option passing

* Fix bugs in `NewtonSolver`, now produces same result as StaticProblem solver

* Static problem working with continuatiuon and newton solver classes

* Fix dumb issue in `_nonlinearCallback`

* Add nonlinear shell regression test

* Fix bug in problem __init__

* Only create solver history if static problem is nonlinear

* Add test for solution history file writing

* Unignore bdf files used for regression tests

* Add nonlinear annular shell example

* Fix bug in hemisphere mesh generation

* Add .pkl files to gitignore

* Tune annular shell example solver settings

* Update nonlinear hemisphere benchmark reference values

* Fix history file test

* Cleanup static problem code, ensure Jacobian is refactored before adjoint in nonlinear case

* Ensure `optLoadScale` is real

* Make shell element test failure messages more helpful

* Turn off excessive printout

* Update some example meshes

* Tuning example solver settings

* Fix some bugs related to restarting the continuation solver from a previous solution

* Add guard against failed linear solves to NewtonSolver

* Fix some NewtonSolverTypos

* Remove solver options from static problem

* Fix bug in basesolver __init__

* Add predictor step to continuation solver

* Remove some relative imports

* Move solverHistory reset to `_initializeSolve`

* Add inner solver default options to continuation solver default options

* Add continuation solver docs page

* Change `_stiffnessUpdateRequired` to `_jacobianUpdateRequired`

* Chane `_factorOnNext` to `_preconditionerUpdateRequired`

* print fix

* clang-format

* Revert "clang-format"

This reverts commit 4c31e15.

* clang-format the right way

* Add option to force at least 1 Newton iteration (fixes complex test failure)

* Rename `newtonSolverMonitorVars` option to `nonlinearSolverMonitorVars` and move to static problem

* Make continuation solver exit if minimum step size fails

* Fix some bugs in static problem option setting

* Make sure nonlinear solver vecs and mats are updated after `_createVariables` is called

* `black .`

* Rename `solveJacLinear`, add some docstrings

* Fix so that continuation solver correctly passes options to inner solver

* Make static problem solve method return a success flag

* Add solve failure checking to mphys

* Starting to add a blade-stiffened shell constitutive model

* Add ContinuationSolver logic for recovering from failed inner solves

* Renaming solver files

* Playing around with solver options in nonlinear examples again

* Give nonlinear shell classes in cython wrapper con and transform attributes

* Revert "Starting to add a blade-stiffened shell constitutive model"

This reverts commit b19ec44.

* minor changes

* Reset predictor states if inner solve fails

* Fix problem initialization bug

* Update nonlinear integration test tolerances

* Fix bug introduced by merge commit

* Change how nonlinear solver options are handled so they don't appear in StaticProblem default options

* Alter `TacsTestElementMatXptSens` to test derivative components individually

* Trying to debug `TacsTestElementMatXptSens` test failure

* Change default complex step size to 1e-200

* fix in `TacsTestElementMatXptSens`

* option name change in NL cantilever example

* Make `solveLinear` a private method

* Remove unused TACS vector

* `black .`

* Implement cheap method for determining if problem is nonlinear

* Move nonlinearity check to pyTACS so it is easily passed to all problem types

* Normalise nonlinearity check by the residual norm

* Fix nonlinear option handling when `options` is `None`

* Fix nonlinearity check for case where r(0) != 0

* Include velocity and accelerations in nonlinearity check

* Fixes in continuation solver to avoid divide by zeros

* Remove unnecessary call to `applyBCs` in  staticProblem

* Add `setBCsInVec` method to pyTACS

* add `setBCs` to `setVariables` so that MPhys wrapper works

* Fix mphys masked_converter imports

* Fix complex type issue in `_checkNonlinearity`

* Revert "Change default complex step size to 1e-200"

This reverts commit 56adb88.

* Fix mat indexing in matrix product sens tests

* Make element test CS step size consistent with size used in element functions

* add method to set multiple options at once

* Add helper function for printing out any modified options

* `black .`

* Remove all logic for automatically passing options from static problem to solvers, remove solver names from their options

* Update cantilever example to use new options system

* Don't create solver history until user has chance to set solver options

* Fix nonlinear integration test

* Documentation improvements

* Rename solver in static problem

* Add section on nonlinear solvers to static problem docs

* Add validation data to nonlinear cantilever example

* Fix local node ID conversion in `getLocalNodeIDsForComps`

* Add `linearityTol` option to pyTACS

* Rename benchmarks

* `black .`

* Make final section of `solve` consistent for linear and nonlinear staticProblems

* Rename `solveNonlinear` to `_solveNonlinearProblem`

* Add `_solveLinearProblem` method

* Rename `res` to `rhs` in `_solveLinear`

* Fixing typo in yield strain

* update nonlinear solver name in examples and test

* Fix reference failure function value

* Rename nonlinear solver in static problem

* Rename self.KSM to self.linearSolver, remove self.newtonSolver

* Move solver history from static problem to solvers

* Don't write history file if don't have a nonlinear Solver

* Update uses of old solver names

* Fix some linter complaints

* Add `printLevel` option to `StaticProblem`

* rename `_solveLinearProblem` and `_solveNonlinearProblem`

* Give nonlinear solvers their own vectors and linear solvers

* Update options used in nonlinear examples

* black formatting

* Fix linear solve call in adjoint solve

* Add numNodes in new element type docstrings

* Revert "Give nonlinear solvers their own vectors and linear solvers"

This reverts commit 92a9f5d.

* Make nonlinear solvers apply boundary conditions to initial state

* Give nonlinear solvers their own vectors and linear solvers

* Minor formatting fix

* Removing `isNonlinear` option from examples

* add `nonlinerSolver` option to staticproblem

* Reducing tol on locally failing test

* Remove BaseSolver from UI documentation

* Fix some mistakes in solver options documentation

---------

Co-authored-by: Tim Brooks <41971846+timryanb@users.noreply.github.com>
  • Loading branch information
A-CGray and timryanb authored Oct 23, 2023
1 parent 03fb1c5 commit c12c5c9
Show file tree
Hide file tree
Showing 52 changed files with 52,925 additions and 260 deletions.
4 changes: 2 additions & 2 deletions Makefile.in.info
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,13 @@ METIS_LIB = ${TACS_DIR}/extern/metis/lib/libmetis.a

# AMD is a set of routines for ordering matrices, included in the SuiteSparse package. It is not required by default.

# SUITESPARSE_DIR = ${TACS_DIR}/extern/SuiteSparse-5.13.0
# SUITESPARSE_DIR = ${TACS_DIR}/extern/SuiteSparse-7.0.1
# The variables below should not need to be altered if you are installing SuiteSparse from the standard release tarball

# SUITESPARSE_CONFIG_DIR = ${SUITESPARSE_DIR}/SuiteSparse_config
# AMD_DIR = ${SUITESPARSE_DIR}/AMD
# AMD_INCLUDE = -I${AMD_DIR}/Include -I${SUITESPARSE_CONFIG_DIR}
# AMD_LIBS = ${AMD_DIR}/Lib/libamd.a ${SUITESPARSE_CONFIG_DIR}/libsuitesparseconfig.a
# AMD_LIBS = ${AMD_DIR}/build/libamd.a ${SUITESPARSE_CONFIG_DIR}/build/libsuitesparseconfig.a
# TACS_DEF += -DTACS_HAS_AMD_LIBRARY

# TECIO is a library for reading and writing tecplot data files, only required for building f5totec, can use either teciosrc or teciompisrc
Expand Down
9 changes: 9 additions & 0 deletions docs/source/pytacs/base_solver.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
BaseSolver
------------------
.. automodule:: tacs.solvers.base

API Reference
^^^^^^^^^^^^^
.. autoclass:: tacs.solvers.BaseSolver
:members:
:inherited-members:
17 changes: 17 additions & 0 deletions docs/source/pytacs/continuation_solver.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
ContinuationSolver
------------------
.. automodule:: tacs.solvers.continuation

Options
^^^^^^^
Options can be set using the :meth:`ContinuationSolver.setOption <tacs.solvers.ContinuationSolver.setOption>` and :meth:`ContinuationSolver.setOptions <tacs.solvers.ContinuationSolver.setOptions>` methods.
Current option values for a class instance can be printed out using the :meth:`ContinuationSolver.printOptions <tacs.solvers.ContinuationSolver.printOptions>` method.
The following options, their default values and descriptions are listed below:

.. program-output:: python -c "from tacs.solvers import ContinuationSolver; ContinuationSolver.printDefaultOptions()"

API Reference
^^^^^^^^^^^^^
.. autoclass:: tacs.solvers.ContinuationSolver
:members:
:inherited-members:
17 changes: 17 additions & 0 deletions docs/source/pytacs/newton_solver.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
NewtonSolver
-------------
.. automodule:: tacs.solvers.newton

Options
^^^^^^^
Options can be set using the :meth:`NewtonSolver.setOption <tacs.solvers.NewtonSolver.setOption>` and :meth:`NewtonSolver.setOptions <tacs.solvers.NewtonSolver.setOptions>` methods.
Current option values for a class instance can be printed out using the :meth:`NewtonSolver.printOptions <tacs.solvers.NewtonSolver.printOptions>` method.
The following options, their default values and descriptions are listed below:

.. program-output:: python -c "from tacs.solvers import NewtonSolver; NewtonSolver.printDefaultOptions()"

API Reference
^^^^^^^^^^^^^
.. autoclass:: tacs.solvers.NewtonSolver
:members:
:inherited-members:
3 changes: 2 additions & 1 deletion docs/source/pytacs/pytacs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ repeated until the optimization criteria are satisfied.

pytacs_module
problems
constraints
solvers
constraints
8 changes: 8 additions & 0 deletions docs/source/pytacs/solvers.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Solver classes
===============

.. toctree::
:maxdepth: 1

continuation_solver
newton_solver
10 changes: 9 additions & 1 deletion docs/source/pytacs/static.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,16 @@ The following options, their default values and descriptions are listed below:

.. program-output:: python -c "from tacs.problems import StaticProblem; StaticProblem.printDefaultOptions()"

Nonlinear solvers
^^^^^^^^^^^^^^^^^
When you create an assembler using a nonlinear element type or constitutive model, any static problems you create will automatically setup a nonlinear solver.
A continuation solver is used to control an adaptive load incrementation process, and a Newton solver is used to solve the nonlinear system of equations at each load increment.
The options for these solvers should be set directly to ``problem.nonlinearSolver`` and ``problem.nonlinearSolver.innerSolver``.
See :ref:`ContinuationSolver <pytacs/continuation_solver:continuationsolver>` and :ref:`NewtonSolver <pytacs/newton_solver:newtonsolver>` for more information.


API Reference
^^^^^^^^^^^^^
.. autoclass:: tacs.problems.StaticProblem
:members:
:inherited-members:
:inherited-members:
178 changes: 178 additions & 0 deletions examples/nonlinear_annulus/analysis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
"""
==============================================================================
Large deformation annular shell
==============================================================================
@File : analysis.py
@Date : 2023/01/25
@Author : Alasdair Christison Gray
@Description : This code runs a geometrically nonlinear analysis of a
annular shell subject to vertical forces at one end. The problem
is taken from section 3.3 of "Popular benchmark problems for geometric
nonlinear analysis of shells" by Sze et al
(https://doi.org/10.1016/j.finel.2003.11.001).
"""

# ==============================================================================
# Standard Python modules
# ==============================================================================
import os

# ==============================================================================
# External Python modules
# ==============================================================================
import numpy as np
from mpi4py import MPI
from pprint import pprint

# ==============================================================================
# Extension modules
# ==============================================================================
from tacs import pyTACS, constitutive, elements, functions


# ==============================================================================
# Constants
# ==============================================================================
COMM = MPI.COMM_WORLD
BDF_FILE = os.path.join(os.path.dirname(__file__), "annulus.bdf")
E = 21e6 # Young's modulus
NU = 0.0 # Poisson's ratio
RHO = 1.0 # density
YIELD_STRESS = 1.0 # yield stress
THICKNESS = 0.03 # Shell thickness

STRAIN_TYPE = "nonlinear"
ROTATION_TYPE = "quadratic"

elementType = None
if STRAIN_TYPE == "linear":
if ROTATION_TYPE == "linear":
elementType = elements.Quad4Shell
elif ROTATION_TYPE == "quadratic":
elementType = elements.Quad4ShellModRot
elif ROTATION_TYPE == "quaternion":
elementType = elements.Quad4ShellQuaternion
elif STRAIN_TYPE == "nonlinear":
if ROTATION_TYPE == "linear":
elementType = elements.Quad4NonlinearShell
elif ROTATION_TYPE == "quadratic":
elementType = elements.Quad4NonlinearShellModRot
elif ROTATION_TYPE == "quaternion":
elementType = elements.Quad4NonlinearShellQuaternion

if elementType is None:
raise RuntimeError("Invalid element type, check STRAIN_TYPE and ROTATION_TYPE.")

# ==============================================================================
# Create pyTACS Assembler and problems
# ==============================================================================
structOptions = {
"printtiming": True,
}
FEAAssembler = pyTACS(BDF_FILE, options=structOptions, comm=COMM)


def elemCallBack(dvNum, compID, compDescript, elemDescripts, specialDVs, **kwargs):
matProps = constitutive.MaterialProperties(rho=RHO, E=E, nu=NU, ys=YIELD_STRESS)
con = constitutive.IsoShellConstitutive(
matProps, t=THICKNESS, tNum=dvNum, tlb=1e-2 * THICKNESS, tub=1e2 * THICKNESS
)
transform = None
element = elementType(transform, con)
tScale = [50.0]
return element, tScale


FEAAssembler.initialize(elemCallBack)

probOptions = {
"nRestarts": 3,
"subSpaceSize": 20,
"printLevel": 1,
}
newtonOptions = {
"MaxIter": 50,
"MaxLinIters": 5,
"UseEW": True,
}
continuationOptions = {
"InitialStep": 0.01,
"TargetIter": 6,
"RelTol": 1e-7,
"UsePredictor": True,
"NumPredictorStates": 8,
}
problem = FEAAssembler.createStaticProblem("Annulus", options=probOptions)
problem.nonlinearSolver.setOptions(continuationOptions)
problem.nonlinearSolver.innerSolver.setOptions(newtonOptions)

# ==============================================================================
# Find tip force points
# ==============================================================================
bdfInfo = FEAAssembler.getBDFInfo()
# cross-reference bdf object to use some of pynastran's advanced features
bdfInfo.cross_reference()
nodeCoords = bdfInfo.get_xyz_in_coord()
nastranNodeNums = list(bdfInfo.node_ids)
SPCNodeIDs = bdfInfo.get_SPCx_node_ids(1)

# The load points lie along the theta = 0 axis, however just searching for points along this axis will return
# both ends of the annulus as it covers a full 360 degrees. So we need to exclude any nodes that are subject to SPC's
nodeTheta = np.arctan2(nodeCoords[:, 1], nodeCoords[:, 0])
loadPointNodeIDs = np.argwhere(nodeTheta == 0.0).flatten() + 1
loadPointNodeIDs = [ii for ii in loadPointNodeIDs if ii not in SPCNodeIDs]

# ==============================================================================
# Add tip loads
# ==============================================================================
PMax = 0.8 # force per length
# Now we need to compute the nodal forces due to the line load, the nodes at the inner and outer radius will be subject to half the load of the other nodes
nodeRadius = np.linalg.norm(nodeCoords, axis=1)
innerRadius = np.min(nodeRadius)
outerRadius = np.max(nodeRadius)
totalForce = PMax * (outerRadius - innerRadius)
loadPointFactors = [
1.0
if nodeRadius[ii - 1] <= (innerRadius + 1e-4)
or nodeRadius[ii - 1] >= (outerRadius - 1e-4)
else 2.0
for ii in loadPointNodeIDs
]
factorSum = np.sum(loadPointFactors)

nodalForces = np.zeros((len(loadPointNodeIDs), 6))
nodalForces[:, 2] = totalForce * np.array(loadPointFactors) / factorSum
problem.addLoadToNodes(loadPointNodeIDs, nodalForces, nastranOrdering=True)

# ==============================================================================
# Add functions
# ==============================================================================

# KS approximation of the maximum failure value
problem.addFunction("KSFailure", functions.KSFailure, ksWeight=80.0, ftype="discrete")

# Maximum displacement in the z-direction (KS with a very large weight to get a true max)
problem.addFunction(
"MaxZDisp",
functions.KSDisplacement,
direction=np.array([0.0, 0.0, 1.0]),
ksWeight=1e20,
ftype="discrete",
)

# Compliance
problem.addFunction("Compliance", functions.Compliance)

# ==============================================================================
# Solve all problems and evaluate functions
# ==============================================================================
funcs = {}
funcsSens = {}
problem.solve()
problem.evalFunctions(funcs)
problem.evalFunctionsSens(funcsSens)
problem.writeSolution(outputDir=os.path.dirname(__file__))
problem.writeSolutionHistory(outputDir=os.path.dirname(__file__))

if COMM.rank == 0:
pprint(funcs)
Loading

0 comments on commit c12c5c9

Please sign in to comment.