Step 3a - Thermal expansion

In this sidebar we'll introduce a thermal expansion eigenstrain. To demonstrate the effect we reshape the domain from step 3 and make it narrow like a cantilever.

#
# Added subdomains and subdomain-specific properties
# https://mooseframework.inl.gov/modules/solid_mechanics/tutorials/introduction/step03.html
#

[GlobalParams<<<{"href": "../../../../syntax/GlobalParams/index.html"}>>>]
  displacements = 'disp_x disp_y'
[]

[Mesh<<<{"href": "../../../../syntax/Mesh/index.html"}>>>]
  [generated]
    type = GeneratedMeshGenerator<<<{"description": "Create a line, square, or cube mesh with uniformly spaced or biased elements.", "href": "../../../../source/meshgenerators/GeneratedMeshGenerator.html"}>>>
    dim<<<{"description": "The dimension of the mesh to be generated"}>>> = 2
    nx<<<{"description": "Number of elements in the X direction"}>>> = 10
    ny<<<{"description": "Number of elements in the Y direction"}>>> = 20
    xmin<<<{"description": "Lower X Coordinate of the generated mesh"}>>> = -0.25
    xmax<<<{"description": "Upper X Coordinate of the generated mesh"}>>> = 0.25
    ymax<<<{"description": "Upper Y Coordinate of the generated mesh"}>>> = 5
  []
  [block1]
    type = SubdomainBoundingBoxGenerator<<<{"description": "Changes the subdomain ID of elements either (XOR) inside or outside the specified box to the specified ID.", "href": "../../../../source/meshgenerators/SubdomainBoundingBoxGenerator.html"}>>>
    input<<<{"description": "The mesh we want to modify"}>>> = generated
    block_id<<<{"description": "Subdomain id to set for inside/outside the bounding box"}>>> = 1
    bottom_left<<<{"description": "The bottom left point (in x,y,z with spaces in-between)."}>>> = '-0.25 0 0'
    top_right<<<{"description": "The bottom left point (in x,y,z with spaces in-between)."}>>> = '0 5 0'
  []
  [block2]
    type = SubdomainBoundingBoxGenerator<<<{"description": "Changes the subdomain ID of elements either (XOR) inside or outside the specified box to the specified ID.", "href": "../../../../source/meshgenerators/SubdomainBoundingBoxGenerator.html"}>>>
    input<<<{"description": "The mesh we want to modify"}>>> = block1
    block_id<<<{"description": "Subdomain id to set for inside/outside the bounding box"}>>> = 2
    bottom_left<<<{"description": "The bottom left point (in x,y,z with spaces in-between)."}>>> = '0 0 0'
    top_right<<<{"description": "The bottom left point (in x,y,z with spaces in-between)."}>>> = '0.25 5 0'
  []

  # select a single node in the center of the bottom boundary
  [pin]
    type = ExtraNodesetGenerator<<<{"description": "Creates a new node set and a new boundary made with the nodes the user provides.", "href": "../../../../source/meshgenerators/ExtraNodesetGenerator.html"}>>>
    input<<<{"description": "The mesh we want to modify"}>>> = block2
    new_boundary<<<{"description": "The names of the boundaries to create"}>>> = pin
    coord<<<{"description": "The nodes with coordinates you want to be in the nodeset. Separate multple coords with ';' (Either this parameter or \"nodes\" must be supplied)."}>>> = '0 0 0'
  []
[]

[AuxVariables<<<{"href": "../../../../syntax/AuxVariables/index.html"}>>>]
  [T]
  []
[]

[AuxKernels<<<{"href": "../../../../syntax/AuxKernels/index.html"}>>>]
  [temperature_ramp]
    type = FunctionAux<<<{"description": "Auxiliary Kernel that creates and updates a field variable by sampling a function through space and time.", "href": "../../../../source/auxkernels/FunctionAux.html"}>>>
    execute_on<<<{"description": "The list of flag(s) indicating when this object should be executed. For a description of each flag, see https://mooseframework.inl.gov/source/interfaces/SetupInterface.html."}>>> = TIMESTEP_BEGIN
    variable<<<{"description": "The name of the variable that this object applies to"}>>> = T
    function<<<{"description": "The function to use as the value"}>>> = 300+5*t
  []
[]

[Physics<<<{"href": "../../../../syntax/Physics/index.html"}>>>/SolidMechanics<<<{"href": "../../../../syntax/Physics/SolidMechanics/index.html"}>>>/QuasiStatic<<<{"href": "../../../../syntax/Physics/SolidMechanics/QuasiStatic/index.html"}>>>]
  [all]
    add_variables<<<{"description": "Add the displacement variables"}>>> = true
    automatic_eigenstrain_names<<<{"description": "Collects all material eigenstrains and passes to required strain calculator within TMA internally."}>>> = true
    generate_output<<<{"description": "Add scalar quantity output for stress and/or strain"}>>> = 'vonmises_stress'
  []
[]

[BCs<<<{"href": "../../../../syntax/BCs/index.html"}>>>]
  [pin_x]
    type = DirichletBC<<<{"description": "Imposes the essential boundary condition $u=g$, where $g$ is a constant, controllable value.", "href": "../../../../source/bcs/DirichletBC.html"}>>>
    variable<<<{"description": "The name of the variable that this residual object operates on"}>>> = disp_x
    boundary<<<{"description": "The list of boundary IDs from the mesh where this object applies"}>>> = pin
    value<<<{"description": "Value of the BC"}>>> = 0
  []
  [bottom_y]
    type = DirichletBC<<<{"description": "Imposes the essential boundary condition $u=g$, where $g$ is a constant, controllable value.", "href": "../../../../source/bcs/DirichletBC.html"}>>>
    variable<<<{"description": "The name of the variable that this residual object operates on"}>>> = disp_y
    boundary<<<{"description": "The list of boundary IDs from the mesh where this object applies"}>>> = bottom
    value<<<{"description": "Value of the BC"}>>> = 0
  []
[]

[Materials<<<{"href": "../../../../syntax/Materials/index.html"}>>>]
  [elasticity]
    type = ComputeIsotropicElasticityTensor<<<{"description": "Compute a constant isotropic elasticity tensor.", "href": "../../../../source/materials/ComputeIsotropicElasticityTensor.html"}>>>
    youngs_modulus<<<{"description": "Young's modulus of the material."}>>> = 1e9
    poissons_ratio<<<{"description": "Poisson's ratio for the material."}>>> = 0.3
  []
  [expansion1]
    type = ComputeThermalExpansionEigenstrain<<<{"description": "Computes eigenstrain due to thermal expansion with a constant coefficient", "href": "../../../../source/materials/ComputeThermalExpansionEigenstrain.html"}>>>
    temperature<<<{"description": "Coupled temperature"}>>> = T
    thermal_expansion_coeff<<<{"description": "Thermal expansion coefficient"}>>> = 0.001
    stress_free_temperature<<<{"description": "Reference temperature at which there is no thermal expansion for thermal eigenstrain calculation"}>>> = 300
    eigenstrain_name<<<{"description": "Material property name for the eigenstrain tensor computed by this model. IMPORTANT: The name of this property must also be provided to the strain calculator."}>>> = thermal_expansion
    block<<<{"description": "The list of blocks (ids or names) that this object will be applied"}>>> = 1
  []
  [expansion2]
    type = ComputeThermalExpansionEigenstrain<<<{"description": "Computes eigenstrain due to thermal expansion with a constant coefficient", "href": "../../../../source/materials/ComputeThermalExpansionEigenstrain.html"}>>>
    temperature<<<{"description": "Coupled temperature"}>>> = T
    thermal_expansion_coeff<<<{"description": "Thermal expansion coefficient"}>>> = 0.002
    stress_free_temperature<<<{"description": "Reference temperature at which there is no thermal expansion for thermal eigenstrain calculation"}>>> = 300
    eigenstrain_name<<<{"description": "Material property name for the eigenstrain tensor computed by this model. IMPORTANT: The name of this property must also be provided to the strain calculator."}>>> = thermal_expansion
    block<<<{"description": "The list of blocks (ids or names) that this object will be applied"}>>> = 2
  []
  [stress]
    type = ComputeLinearElasticStress<<<{"description": "Compute stress using elasticity for small strains", "href": "../../../../source/materials/ComputeLinearElasticStress.html"}>>>
  []
[]

[Executioner<<<{"href": "../../../../syntax/Executioner/index.html"}>>>]
  type = Transient
  solve_type = NEWTON
  petsc_options_iname = '-pc_type'
  petsc_options_value = 'lu'
  end_time = 5
  dt = 1
[]

[Outputs<<<{"href": "../../../../syntax/Outputs/index.html"}>>>]
  exodus<<<{"description": "Output the results using the default settings for Exodus output."}>>> = true
[]
(moose/modules/solid_mechanics/tutorials/introduction/mech_step03a.i)

Input file

Mesh

Note the xmin and xmax upper and lower x-dimension extents are supplied here. The origin will lie in the center of the bottom boundary of this generated mesh.

New is the ExtraNodesetGenerator which we append to the existing chain of mesh generators. This generator allows us to create a new nodeset containing all nodes located at the coordinates the user specified in "coord". Here we create a nodeset containing the single node at the origin. You need to make sure that a node actually exists at each specified coordinate! We call this nodeset pin for obvious reasons and will use it below in the BCs.

AuxVariables

We introduce a new auxiliary variable T (for temperature). Auxiliary variables are variables we're not solving for, but are computing directly. To simplify this step we are not solving a heat conduction problem, but instead just prescribing a global temperature that is rising with time (see next section). Auxiliary variables can be coupled in everywhere regular (so called nonlinear) variables are coupled. They are a great tool for simplifying a model during development.

AuxKernels

The FunctionAux AuxKernel can set an AuxVariable to a function of space and time. Note the "execute_on" parameter that is available in many MOOSE systems. Here we skip execution during LINEAR and NON_LINEAR iterations and only update the variable value at the beginning of the timestep.

SolidMechanics QuasiStatic Physics

We've added the "automatic_eigenstrain_names" parameter to the quasi-static physics. With this option enabled the quasi-static physics will try to automatically detect all material objects that provide eigenstrain properties. This works well for most scenarios. Note that MOOSE will print a list of detected eigenstrain names very early in its console output. Look for


*** Automatic Eigenstrain Names ***
all: thermal_expansion

when you run this example. Here all is the quasi-static physics block name and thermal_expansion is the "eigenstrain_name" parameter value for the two eigenstrain materials below. The action correctly detected it and verified that eigenstrains are provided on all subdomains covered by the quasi-static physics block. To manually supply the eigenstrain material properties use the "eigenstrain_names" parameter. Like so


[Physics/SolidMechanics/QuasiStatic]
  [all]
    add_variables = true
    eigenstrain_names = 'thermal_expansion'
    generate_output = 'vonmises_stress'
  []
[]

BCs

We have changed the way we apply the boundary condition on the x displacement variable disp_x to only constrain this variable at the pin "boundary". Above we defined this boundary as the nodeset containing the single node at (0,0,0).

Pinning at a single point is less restrictive and allows the model to expand in x-direction along the bottom boundary (which will happen due to isotropic thermal expansion). Yet we still remove all rigid body modes

  • Translation, because we fix both x and y displacement on at least one node

  • Rotation, because we fix y displacement on another node (actually on all nodes along the bottom boundary)

Pinning nodes to remove rigid body modes is an important tool to create mechanics simulations that converge. The presence of rigid body modes will lead to non-convergence. BCs on single nodes rather than whole boundaries can help avoid overconstraining your problems. Keep in mind that to remove all 6 rigid body modes in a 3D simulation you need to apply BCs on at least 3 nodes (which cannot be co-linear). One node will have to be constrained in 3 direction, one in 2 and one in just one direction. The first node will remove three translation modes. The second node will remove two rotational modes (and will establish an axis of rotation). The third node will remove that final rotational mode.

Materials

An "Eigenstrain" in MOOSE is a stress free strain. It is an intrinsic shape change of a volume element due to effects like thermal expansion, swelling, diffusion of over/undersized solutes, etc.

We use two ComputeThermalExpansionEigenstrain objects to compute an eigenstrain tensor in each of the two subdomains. The "stress_free_temperature" is set to 300K, which is the initial temperature set for the T AuxVariable. At this temperature we assume the eigenstrain to be zero (with contraction at temperatures lower than 300K and expansion above). The "thermal_expansion_coeff" is chosen differently on the two subdomains. This effectively models a bimetallic strip.

Questions

Expected outcome

Think about what you expect to happen when you run the input.

Click here for the answer.

Overconstraining

Apply the disp_x boundary condition to the entire bottom surface again and observe what happens. Undo that change before you move on to the next question.

Click here for the answer.

Constraining even less

In the original input we're fixing the y displacement to 0 in the entire bottom boundary. Try to relax this constraint a bit try to add a second single node boundary using the ExtraNodesetGenerator (chained in after the pin generator). Use one of the two bottom corner nodes. Now think about where you have to apply the disp_y != 0 boundary condition.

Click here for the answer.

Once you've answered the questions and run this example we will move on to Step 4 and setup a cantilever problem that prepares us for contact.