Materials 3: Oriented materials
So far, we’ve discussed different symmetry classes of elastic and acoustic materials. These symmetry classes are defined by specific mathematical transformations (e.g. rotations, reflections) that leave the material properties unchanged. For example, a hexagonal (transversely isotropic) material doesn’t change its properties when rotated around the axis of symmetry.
In Salvus material, by default, the axes of the symmetry classes are aligned with the coordinate axes. However, in many cases, an actual material may be oriented arbitrarily in space, or even spatially heterogeneously oriented.:
- Geological formations - Layered sedimentary rock formations frequently have tilted or folded layers
- Composite materials - Engineered materials like fiber composites have specific fiber orientations
- Crystalline structures - Natural crystals and minerals have preferred orientations
- Fluid-filled fractures - Aligned fluid-filled cracks or fractures create directional anisotropy
Being able to specify material orientations allows modelers to represent these real-world scenarios more accurately, leading to more precise simulation results.
Material transformations and symmetry classes
The material module in Salvus allows for the orientation of materials to be specified. One very important note however is that if a material like hexagonal is oriented such that the symmetry axis or planes do not anymore align with the coordinate axes, the material is no longer hexagonal in the global coordinate system. This means that after applying an orientation, materials in general will be triclinic (elastic) or orthotropic (acoustic).
For example, if we take a VTI (Vertically Transverse Isotropic) material with its symmetry axis aligned with the z-axis, and tilt it by some angle, the resulting material will need to be represented in a lower symmetry class (triclinic) when expressed in the global coordinate system.
A big caveat of using oriented materials is that the module does not easily support orienting materials for 2 dimensions. If this is a feature you are interested in, please reach out to support to get you underway on a per-case basis, and to indicate there is interest in this feature being part of the module.
from salvus.material import elastic, orientation
import numpy as np
from salvus.material._details.material import to_solver_form
np.set_printoptions(precision=2)Creating orientations
Salvus provides multiple ways to specify the orientation of a material. These different methods allow you to describe the orientation in terms that make the most sense for your particular application or dataset.
AzimuthDip
The AzimuthDip orientation is one of the more common ways to specify
material orientation. It uses:
- Azimuth: The angle (in degrees) measured from the first global axis (the x-axis) in the horizontal plane.
- Dip: The angle (in degrees) measured downward from the horizontal plane.
This parameterization is particularly useful in geophysics and structural geology where field measurements are often reported in these terms.
orientation_1 = orientation.AzimuthDip.from_params(azimuth=0, dip=22.5)
orientation_1With the following properties:
| Field | Type | Value (or range) |
|---|---|---|
| AZIMUTH | constant | 0 |
| DIP | constant | 22 |
salvus.material.orientation.AzimuthDip
All orientations know how to generate a rotation matrix that can be used to rotate a material tensor (which is done internally in Salvus later):
orientation_1.generate_rotation_matrix()array([[[ 0.92, 0. , 0.38],
[ 0. , 1. , 0. ],
[-0.38, 0. , 0.92]]])The visualization shows how the local coordinate system is oriented relative to the global coordinate system. The local z-axis (blue) is tilted at 22.5° from the global z-axis, towards the global x-axis (red).
_ = orientation.visualize_local_bases(orientation_1)We can also specify spatially varying orientations by providing arrays for azimuth and dip:
orientation_2 = orientation.AzimuthDip.from_params(
azimuth=0, dip=np.array([0, 11.25, 22.5, 33.75, 45.0])
)
# The semi-colon at the end of the line suppresses the output in Jupyter,
# such that the figure is not printed twice.
_ = orientation.visualize_local_bases(orientation_2)That was different dip angles in the global x-z plane. Specifying varying azimuths would look like this:
orientation_3 = orientation.AzimuthDip.from_params(
azimuth=np.array([0, 30, 60, 90]), dip=33
)
_ = orientation.visualize_local_bases(orientation_3)AxesDips
Another way to specify orientation is using the AxesDips class, which
allows you to specify:
- dip_x: The dip angle of the x-axis from the horizontal plane
- dip_y: The dip angle of the y-axis from the horizontal plane
This parameterization can be more intuitive when working with materials where you know the orientation of specific planes or axes relative to the global coordinate system.
orientation_4 = orientation.AxesDips.from_params(dip_x=22.5, dip_y=12.5)
orientation_4With the following properties:
| Field | Type | Value (or range) |
|---|---|---|
| DIP_X | constant | 22 |
| DIP_Y | constant | 12 |
salvus.material.orientation.AxesDips
What’s important to note is that the dip_x and dip_y angles don’t
specify the total dip of the local horizontal plane, except when one of them
is 0:
orientation_5 = orientation.AxesDips.from_params(dip_x=22.5, dip_y=0)
np.allclose(
orientation_1.generate_rotation_matrix(),
orientation_5.generate_rotation_matrix(),
)True
# But when dip_x and dip_y are both non-zero:
np.allclose(
orientation_1.generate_rotation_matrix(),
orientation_4.generate_rotation_matrix(),
)False
This effect can be very deceptive. For relatively steep dipping planes, the visual difference is small:
orientation_5a = orientation.AxesDips.from_params(dip_x=33, dip_y=33)
orientation_5aWith the following properties:
| Field | Type | Value (or range) |
|---|---|---|
| DIP_X | constant | 33 |
| DIP_Y | constant | 33 |
salvus.material.orientation.AxesDips
orientation_5b = orientation.AzimuthDip.from_params(azimuth=45, dip=33)
orientation_5bWith the following properties:
| Field | Type | Value (or range) |
|---|---|---|
| AZIMUTH | constant | 45 |
| DIP | constant | 33 |
salvus.material.orientation.AzimuthDip
orientation.visualize_local_bases(orientation_5a)
orientation.visualize_local_bases(orientation_5b)This unapparent difference is an effect of the projection on the coordinate planes. When directly comparing the two orientations’ rotation matrices, we see that they are indeed different:
np.allclose(
orientation_5a.generate_rotation_matrix(),
orientation_5b.generate_rotation_matrix(),
)False
A final test is to convert one of the orientations into the other’s system:
orientation.AzimuthDip.from_material(orientation_5a).DIP.p42.564398528805846
orientation_5b.DIP.p33.0
Ambiguous orientations
The AxesDips and AzimuthDip orientations are unfortunately not always
sufficient to describe the orientation of a material. For example, if we
have a material that is not hexagonal, but orthotropic, monoclinic or
triclinic, we need to also keep track of the orientation of the material’s
horizontal rotation about the local z-axis.
Both the aforementioned orientations are not able to do this, and will simply ambiguously transform local_x and local_y to lie in the dipping plane.
AzimuthDipRake
A further way to specify orientation is using the AzimuthDipRake
orientation, which has the same definition as AzimuthDip, but adds a
rake angle. The rake angle is defined as an angle measured from the
horizontal on the dipping plane (strike) for local y, or as the angle between
the down direction on the dipping plane and the local x-axis. This way, when
“facing” in the down direction on the dipping plane, local x is pointing in
your view direction, and local y is pointing to your left. The rake angle is
measured in the plane of the dip.
This is a common way to specify the orientation of faults or fractures in geophysics, where the rake angle describes the direction of slip or movement along the fault plane.
orientation_6 = orientation.AzimuthDipRake.from_params(
azimuth=0, # Dip towards the x-axis
dip=22.5,
# Local x as to make these angles with the local down, in-plane.
rake=np.linspace(0, 30, 5),
)
orientation.visualize_local_bases(orientation_6)DirectOrthonormalBasis
Finally, we can also specify the orientation using a DirectOrthonormalBasis
class. This class allows you to specify the local coordinate system using
three orthonormal basis vectors. This is a more general way to specify
orientation and can be useful when you have a specific coordinate system in
mind or when the other parameterizations are not sufficient.
orientation_7 = orientation.DirectOrthonormalBasis.from_basis_vectors(
local_x=np.array([0, 1.0, 0]),
local_y=np.array([-1.0, 0.0, 0]),
local_z=np.array([0, 0, 1.0]),
)
orientation.visualize_local_bases(orientation_7)Note that this orientation also applies the Gram-Schmidt process to ensure that the basis vectors are orthonormal. This means that the vectors are guaranteed to be orthogonal and have unit length, but the input vectors are not necessarily respected.
orientation_8 = orientation.DirectOrthonormalBasis.from_basis_vectors(
local_x=np.array([0, 1.0, 0]),
local_y=np.array([-1.0, 0.0, 0]),
local_z=np.array([0.2, 0.2, 1.0]),
skip_orthonormalization=False,
)
orientation.visualize_local_bases(orientation_8)
orientation_8.get_basis_vectors()(array([[ 0. , 0.98, -0.19]]), array([[-0.98, 0.04, 0.19]]), array([[0.2 , 0.19, 0.96]]))
Applying orientations to materials
Now that we’ve explored how to create different orientations, let’s see how to apply them to materials. When we apply an orientation to a material, we’re essentially rotating the material’s properties to align with our specified orientation.
VTI materials and dipping planes
Here, we’ll start with a VTI (Vertically Transverse Isotropic) material defined using Thomsen parameters. Thomsen parameters are commonly used in exploration geophysics to describe weak anisotropy:
- epsilon (ε): Controls the difference between horizontal and vertical P-wave velocities
- delta (δ): Controls the angular dependence of P-wave velocity
- gamma (γ): Controls the difference between horizontal and vertical S-wave velocities
First, let’s create our VTI material:
elastic_vti_material = elastic.transversely_isotropic.Thomsen.from_params(
rho=2500, vp=5000, vs=3500, epsilon=0.23, delta=0.4, gamma=0.2
)
elastic_vti_materialWith the following properties:
| Field | Type | Value (or range) |
|---|---|---|
| RHO | constant | 2.5e+03 |
| VP | constant | 5e+03 |
| VS | constant | 3.5e+03 |
| DELTA | constant | 0.4 |
| GAMMA | constant | 0.2 |
| EPSILON | constant | 0.23 |
salvus.material.elastic.hexagonal.Thomsen
elastic_vti_material.to_tensor_components()With the following properties:
| Field | Type | Value (or range) |
|---|---|---|
| RHO | constant | 2.5e+03 |
| C11 | constant | 9.1e+10 |
| C12 | constant | 5.5e+09 |
| C13 | constant | 2e+10 |
| C33 | constant | 6.2e+10 |
| C44 | constant | 3.1e+10 |
The averages of the stiffness tensor are the following, using the symmetry definition of this material to fill in implied positions. Empty positions are zero but not stored in memory for this material:
| Cij | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 1 | 9.1e+10 | 5.5e+09 | 2e+10 | - | - | - |
| 2 | 5.5e+09 | 9.1e+10 | 2e+10 | - | - | - |
| 3 | 2e+10 | 2e+10 | 6.2e+10 | - | - | - |
| 4 | - | - | - | 3.1e+10 | - | - |
| 5 | - | - | - | - | 3.1e+10 | - |
| 6 | - | - | - | - | - | 4.3e+10 |
salvus.material.elastic.hexagonal.TensorComponents
When we apply an orientation to our VTI material, we’re essentially tilting its symmetry axis. In this case, we’re using our AzimuthDip orientation to tilt the symmetry axis by 22.5 degrees from the vertical:
orientation_1 = orientation.AzimuthDip.from_params(azimuth=0, dip=22.5)
elastic_vti_material_oriented = elastic_vti_material.with_orientation(
orientation_1
)
elastic_vti_material_orientedWith the following properties:
| Field | Type | Value (or range) |
|---|---|---|
| RHO | constant | 2.5e+03 |
| VP | constant | 5e+03 |
| VS | constant | 3.5e+03 |
| DELTA | constant | 0.4 |
| GAMMA | constant | 0.2 |
| EPSILON | constant | 0.23 |
| AZIMUTH | constant | 0 |
| DIP | constant | 22 |
abc.salvus.material.orientation.elastic.hexagonal.Thomsen
Now let’s convert our oriented material to a solver-compatible form. Salvus requires materials to be in one of its supported forms for simulation. When we apply an orientation to a material, we need to convert it to a form that the solver can understand - in this case, a triclinic tensor components material.
# to_solver_form(m=elastic_vti_material) # Won't work, nothing to do!
elastic_vti_material_oriented_sf = to_solver_form(
m=elastic_vti_material_oriented
)
elastic_vti_material_oriented_sfWith the following properties:
| Field | Type | Value (or range) |
|---|---|---|
| RHO | constant | 2.5e+03 |
| C11 | constant | 8.8e+10 |
| C12 | constant | 7.7e+09 |
| C13 | constant | 1.9e+10 |
| C14 | constant | 0 |
| C15 | constant | -3.9e+09 |
| C16 | constant | 0 |
| C22 | constant | 9.1e+10 |
| C23 | constant | 1.8e+10 |
| C24 | constant | 0 |
| C25 | constant | 5.3e+09 |
| C26 | constant | 0 |
| C33 | constant | 6.8e+10 |
| C34 | constant | 0 |
| C35 | constant | -6.3e+09 |
| C36 | constant | 0 |
| C44 | constant | 3.2e+10 |
| C45 | constant | 0 |
| C46 | constant | -4.3e+09 |
| C55 | constant | 2.9e+10 |
| C56 | constant | 0 |
| C66 | constant | 4.1e+10 |
The averages of the stiffness tensor are the following, using the symmetry definition of this material to fill in implied positions. Empty positions are zero but not stored in memory for this material:
| Cij | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 1 | 8.8e+10 | 7.7e+09 | 1.9e+10 | 0 | -3.9e+09 | 0 |
| 2 | 7.7e+09 | 9.1e+10 | 1.8e+10 | 0 | 5.3e+09 | 0 |
| 3 | 1.9e+10 | 1.8e+10 | 6.8e+10 | 0 | -6.3e+09 | 0 |
| 4 | 0 | 0 | 0 | 3.2e+10 | 0 | -4.3e+09 |
| 5 | -3.9e+09 | 5.3e+09 | -6.3e+09 | 0 | 2.9e+10 | 0 |
| 6 | 0 | 0 | 0 | -4.3e+09 | 0 | 4.1e+10 |
salvus.material.elastic.triclinic.TensorComponents
elastic_vti_material_oriented_sf.to_wavelength_oracle()Constant parameter Spatially constant Value: 3.4e+03
isinstance(
elastic_vti_material_oriented_sf, elastic.triclinic.TensorComponents
)True
Rotating in the symmetry plane does not break the symmetry of the tensor, but will downcast the material to triclinic. Downcasting means that the material is no longer in the original symmetry class (VTI) but is now represented in a lower symmetry class.
orientation_rotation_vti = (
orientation.DirectOrthonormalBasis.from_basis_vectors(
local_x=np.array([0, 1.0, 0]),
local_y=np.array([-1.0, 0, 0]),
local_z=np.array([0, 0, 1.0]),
)
)
to_solver_form(elastic_vti_material.with_orientation(orientation_rotation_vti))With the following properties:
| Field | Type | Value (or range) |
|---|---|---|
| RHO | constant | 2.5e+03 |
| C11 | constant | 9.1e+10 |
| C12 | constant | 5.5e+09 |
| C13 | constant | 2e+10 |
| C14 | constant | 0 |
| C15 | constant | 0 |
| C16 | constant | 0 |
| C22 | constant | 9.1e+10 |
| C23 | constant | 2e+10 |
| C24 | constant | 0 |
| C25 | constant | 0 |
| C26 | constant | 0 |
| C33 | constant | 6.2e+10 |
| C34 | constant | 0 |
| C35 | constant | 0 |
| C36 | constant | 0 |
| C44 | constant | 3.1e+10 |
| C45 | constant | 0 |
| C46 | constant | 0 |
| C55 | constant | 3.1e+10 |
| C56 | constant | 0 |
| C66 | constant | 4.3e+10 |
The averages of the stiffness tensor are the following, using the symmetry definition of this material to fill in implied positions. Empty positions are zero but not stored in memory for this material:
| Cij | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 1 | 9.1e+10 | 5.5e+09 | 2e+10 | 0 | 0 | 0 |
| 2 | 5.5e+09 | 9.1e+10 | 2e+10 | 0 | 0 | 0 |
| 3 | 2e+10 | 2e+10 | 6.2e+10 | 0 | 0 | 0 |
| 4 | 0 | 0 | 0 | 3.1e+10 | 0 | 0 |
| 5 | 0 | 0 | 0 | 0 | 3.1e+10 | 0 |
| 6 | 0 | 0 | 0 | 0 | 0 | 4.3e+10 |
salvus.material.elastic.triclinic.TensorComponents
Orthotropic materials and rake angles
Now let’s create an orthotropic material using the AzimuthDipRake
orientation. This material has different properties in three orthogonal
directions, and we can specify its orientation using the azimuth, dip, and
rake angles.
# Completely fictitious material properties
orthotropic_material = elastic.orthotropic.EngineeringConstants.from_params(
rho=2500,
e1=50e9,
e2=30e9,
e3=20e9,
v12=0.2,
v13=0.1,
v23=0.3,
g12=10e9,
g13=5e9,
g23=2e9,
)
orthotropic_materialWith the following properties:
| Field | Type | Value (or range) |
|---|---|---|
| RHO | constant | 2.5e+03 |
| E1 | constant | 5e+10 |
| E2 | constant | 3e+10 |
| E3 | constant | 2e+10 |
| G12 | constant | 1e+10 |
| G13 | constant | 5e+09 |
| G23 | constant | 2e+09 |
| V12 | constant | 0.2 |
| V13 | constant | 0.1 |
| V23 | constant | 0.3 |
salvus.material.elastic.orthotropic.EngineeringConstants
# Let's visualize the entries of the stiffness tensor as well:
orthotropic_material.to_tensor_components()With the following properties:
| Field | Type | Value (or range) |
|---|---|---|
| RHO | constant | 2.5e+03 |
| C11 | constant | 5.2e+10 |
| C12 | constant | 7.3e+09 |
| C13 | constant | 3.5e+09 |
| C22 | constant | 3.3e+10 |
| C23 | constant | 6.9e+09 |
| C33 | constant | 2.2e+10 |
| C44 | constant | 2e+09 |
| C55 | constant | 5e+09 |
| C66 | constant | 1e+10 |
The averages of the stiffness tensor are the following, using the symmetry definition of this material to fill in implied positions. Empty positions are zero but not stored in memory for this material:
| Cij | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 1 | 5.2e+10 | 7.3e+09 | 3.5e+09 | - | - | - |
| 2 | 7.3e+09 | 3.3e+10 | 6.9e+09 | - | - | - |
| 3 | 3.5e+09 | 6.9e+09 | 2.2e+10 | - | - | - |
| 4 | - | - | - | 2e+09 | - | - |
| 5 | - | - | - | - | 5e+09 | - |
| 6 | - | - | - | - | - | 1e+10 |
salvus.material.elastic.orthotropic.TensorComponents
We now first start off easy: a material with simply a 90 degree rotation about the (local) z-axis. Let’s see what this does to the stiffness tensor:
orientation_orthotropic_1 = orientation.AzimuthDipRake.from_params(
azimuth=0, dip=0, rake=90
)
orthotropic_material_oriented = orthotropic_material.with_orientation(
orientation_orthotropic_1
)
to_solver_form(orthotropic_material_oriented)With the following properties:
| Field | Type | Value (or range) |
|---|---|---|
| RHO | constant | 2.5e+03 |
| C11 | constant | 3.3e+10 |
| C12 | constant | 7.3e+09 |
| C13 | constant | 6.9e+09 |
| C14 | constant | 0 |
| C15 | constant | 0 |
| C16 | constant | -3.5e-07 |
| C22 | constant | 5.2e+10 |
| C23 | constant | 3.5e+09 |
| C24 | constant | 0 |
| C25 | constant | 0 |
| C26 | constant | 1.5e-06 |
| C33 | constant | 2.2e+10 |
| C34 | constant | 0 |
| C35 | constant | 0 |
| C36 | constant | -2.1e-07 |
| C44 | constant | 5e+09 |
| C45 | constant | 1.8e-07 |
| C46 | constant | 0 |
| C55 | constant | 2e+09 |
| C56 | constant | 0 |
| C66 | constant | 1e+10 |
The averages of the stiffness tensor are the following, using the symmetry definition of this material to fill in implied positions. Empty positions are zero but not stored in memory for this material:
| Cij | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 1 | 3.3e+10 | 7.3e+09 | 6.9e+09 | 0 | 0 | -3.5e-07 |
| 2 | 7.3e+09 | 5.2e+10 | 3.5e+09 | 0 | 0 | 1.5e-06 |
| 3 | 6.9e+09 | 3.5e+09 | 2.2e+10 | 0 | 0 | -2.1e-07 |
| 4 | 0 | 0 | 0 | 5e+09 | 1.8e-07 | 0 |
| 5 | 0 | 0 | 0 | 1.8e-07 | 2e+09 | 0 |
| 6 | -3.5e-07 | 1.5e-06 | -2.1e-07 | 0 | 0 | 1e+10 |
salvus.material.elastic.triclinic.TensorComponents
This resulting material has the entries for C11 and C22 swapped, C13 and C23 swapped, and C44 and C55 swapped. Additionally, some numerical noise has entered some of the off-diagonal terms. This is not an issue when simulating with Salvus, but can be manually muted if desired.
To map this material back to an orthotropic form (and mute numerical noise), Salvus will require you to forcefully discard information, as strictly speaking, the material is now triclinic.
elastic.orthotropic.EngineeringConstants.from_material(
to_solver_form(orthotropic_material_oriented),
reduction_method="remove-components",
)With the following properties:
| Field | Type | Value (or range) |
|---|---|---|
| RHO | constant | 2.5e+03 |
| E1 | constant | 3e+10 |
| E2 | constant | 5e+10 |
| E3 | constant | 2e+10 |
| G12 | constant | 1e+10 |
| G13 | constant | 2e+09 |
| G23 | constant | 5e+09 |
| V12 | constant | 0.12 |
| V13 | constant | 0.3 |
| V23 | constant | 0.1 |
salvus.material.elastic.orthotropic.EngineeringConstants
Also note that remove-components only works if the entries in the to-be removed components are actually close to zero. This is apparent when we try the same approach with a less than 90 degree rotation, under which the material doesn’t remain orthotropic in the global coordinate system:
orthotropic_material_oriented_45 = orthotropic_material.with_orientation(
orientation.AzimuthDipRake.from_params(azimuth=0, dip=0, rake=45)
)
to_solver_form(orthotropic_material_oriented_45)With the following properties:
| Field | Type | Value (or range) |
|---|---|---|
| RHO | constant | 2.5e+03 |
| C11 | constant | 3.5e+10 |
| C12 | constant | 1.5e+10 |
| C13 | constant | 5.2e+09 |
| C14 | constant | 0 |
| C15 | constant | 0 |
| C16 | constant | 4.7e+09 |
| C22 | constant | 3.5e+10 |
| C23 | constant | 5.2e+09 |
| C24 | constant | 0 |
| C25 | constant | 0 |
| C26 | constant | 4.7e+09 |
| C33 | constant | 2.2e+10 |
| C34 | constant | 0 |
| C35 | constant | 0 |
| C36 | constant | -1.7e+09 |
| C44 | constant | 3.5e+09 |
| C45 | constant | 1.5e+09 |
| C46 | constant | 0 |
| C55 | constant | 3.5e+09 |
| C56 | constant | 0 |
| C66 | constant | 1.8e+10 |
The averages of the stiffness tensor are the following, using the symmetry definition of this material to fill in implied positions. Empty positions are zero but not stored in memory for this material:
| Cij | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 1 | 3.5e+10 | 1.5e+10 | 5.2e+09 | 0 | 0 | 4.7e+09 |
| 2 | 1.5e+10 | 3.5e+10 | 5.2e+09 | 0 | 0 | 4.7e+09 |
| 3 | 5.2e+09 | 5.2e+09 | 2.2e+10 | 0 | 0 | -1.7e+09 |
| 4 | 0 | 0 | 0 | 3.5e+09 | 1.5e+09 | 0 |
| 5 | 0 | 0 | 0 | 1.5e+09 | 3.5e+09 | 0 |
| 6 | 4.7e+09 | 4.7e+09 | -1.7e+09 | 0 | 0 | 1.8e+10 |
salvus.material.elastic.triclinic.TensorComponents
try:
elastic.orthotropic.EngineeringConstants.from_material(
to_solver_form(orthotropic_material_oriented_45),
reduction_method="remove-components",
)
except TypeError as e:
print(e)Passed material of type <class 'salvus.material.elastic.triclinic.TensorComponents'> can't be treated as <class 'salvus.material.elastic.orthotropic.TensorComponents'>, as it does not respect the symmetry. The component C16 should be zero, but it is not.
The “force” method will work, but information will be lost and the material will be altered:
elastic.orthotropic.EngineeringConstants.from_material(
to_solver_form(orthotropic_material_oriented_45), reduction_method="force"
)With the following properties:
| Field | Type | Value (or range) |
|---|---|---|
| RHO | constant | 2.5e+03 |
| E1 | constant | 2.8e+10 |
| E2 | constant | 2.8e+10 |
| E3 | constant | 2e+10 |
| G12 | constant | 1.8e+10 |
| G13 | constant | 3.5e+09 |
| G23 | constant | 3.5e+09 |
| V12 | constant | 0.4 |
| V13 | constant | 0.14 |
| V23 | constant | 0.14 |
salvus.material.elastic.orthotropic.EngineeringConstants
Finally, let’s see how an orthotropic material can map into a fully triclinic material.
orthotropic_material_oriented_dramatic = orthotropic_material.with_orientation(
orientation.AzimuthDipRake.from_params(azimuth=22.5, dip=30, rake=45)
)
to_solver_form(orthotropic_material_oriented_dramatic)With the following properties:
| Field | Type | Value (or range) |
|---|---|---|
| RHO | constant | 2.5e+03 |
| C11 | constant | 2.5e+10 |
| C12 | constant | 9.5e+09 |
| C13 | constant | 1.1e+10 |
| C14 | constant | 1.6e+09 |
| C15 | constant | -5.6e+09 |
| C16 | constant | -1.6e+09 |
| C22 | constant | 4.2e+10 |
| C23 | constant | 7.6e+09 |
| C24 | constant | -8.2e+09 |
| C25 | constant | -3e+09 |
| C26 | constant | 3.6e+09 |
| C33 | constant | 1.9e+10 |
| C34 | constant | -7.8e+08 |
| C35 | constant | 9.5e+08 |
| C36 | constant | 7.6e+08 |
| C44 | constant | 8.8e+09 |
| C45 | constant | 2.3e+09 |
| C46 | constant | -4.2e+09 |
| C55 | constant | 7.7e+09 |
| C56 | constant | -6.4e+08 |
| C66 | constant | 1.1e+10 |
The averages of the stiffness tensor are the following, using the symmetry definition of this material to fill in implied positions. Empty positions are zero but not stored in memory for this material:
| Cij | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 1 | 2.5e+10 | 9.5e+09 | 1.1e+10 | 1.6e+09 | -5.6e+09 | -1.6e+09 |
| 2 | 9.5e+09 | 4.2e+10 | 7.6e+09 | -8.2e+09 | -3e+09 | 3.6e+09 |
| 3 | 1.1e+10 | 7.6e+09 | 1.9e+10 | -7.8e+08 | 9.5e+08 | 7.6e+08 |
| 4 | 1.6e+09 | -8.2e+09 | -7.8e+08 | 8.8e+09 | 2.3e+09 | -4.2e+09 |
| 5 | -5.6e+09 | -3e+09 | 9.5e+08 | 2.3e+09 | 7.7e+09 | -6.4e+08 |
| 6 | -1.6e+09 | 3.6e+09 | 7.6e+08 | -4.2e+09 | -6.4e+08 | 1.1e+10 |
salvus.material.elastic.triclinic.TensorComponents
This even produces negative moduli in the off-diagonal terms!
In the final tutorial of this series, we will explore how to set up a simulation using the materials we’ve created.