Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linear elasticity #22

Closed
wants to merge 53 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
d2823ff
[WIP] Initialized linear_elasticity.py.
Smantii Jun 6, 2023
4457e10
Extended and tested flat operator to discrete tensor fields.
Smantii Jun 7, 2023
b2d4084
Extended and tested coboundary and hodge star to vector-valued cochains.
Smantii Jun 7, 2023
355492c
[WIP] Continuing with the class LinearElasticity().
Smantii Jun 7, 2023
c713c7b
Fixed various issues in vector.py and simplex.py. Added documentation…
Smantii Jun 8, 2023
99d157c
Fixed linting.
Smantii Jun 8, 2023
22af2eb
Fixed various typos.
Smantii Jun 8, 2023
6861810
[WIP] Implementing test_linear_elasticity.py
Smantii Jun 8, 2023
e7745ed
[WIP] Fixed various issues in linear elasticity. Still work in progress.
Smantii Jun 8, 2023
9ae4f0a
[WIP] Fixed metric computation bug. Another bug still occurs.
Smantii Jun 9, 2023
cf38965
[WIP] Small fix.
Smantii Jun 9, 2023
c41c07e
Fixed computation of 2D metric.
alucantonio Jun 13, 2023
33f4c15
[WIP] Added ad-hoc implementation of zero forces on free boundary in …
Smantii Jun 15, 2023
71ebda4
Added example notebook on linear elasticity.
alucantonio Jun 15, 2023
c2b3538
Merging.
alucantonio Jun 15, 2023
513e715
Fixed tests' problem.
Smantii Jun 16, 2023
4b4aa13
[WIP] Implemented set_boundary_tractions. Improved linear_elasticity …
Smantii Jun 16, 2023
b509196
Improved documentation of linear_elasticity.py
Smantii Jun 16, 2023
ef92760
Fixed linting.
Smantii Jun 16, 2023
a5249ce
Poisson 3D notebook. Fixed indices issues in dual volume calculations…
alucantonio Jun 16, 2023
33f9f24
Merging changes.
alucantonio Jun 16, 2023
a1890c5
Changes to dependencies.
alucantonio Jun 16, 2023
10d94c7
[WIP] Working on test_linear_elasticity.py
Smantii Jun 19, 2023
c513c66
Fixed and tested linear_elasticity.py
Smantii Jun 20, 2023
d486101
[WIP] Started to working on boundary condition generalization.
Smantii Jun 20, 2023
0b04115
Added example notebook on Poisson 3D transient.
alucantonio Jun 20, 2023
3372830
Fixing linear elasticity example notebook.
alucantonio Jun 22, 2023
b3257e4
[WIP] Rewrited test_linear_elasticity to solve the uniaxial tension p…
Smantii Jun 22, 2023
2bbe9dc
Fixed jupyter notebook. Improved docs of util.py and simplex.py
Smantii Jun 22, 2023
51d7702
[WIP] Fixes to dual volumes structures. Tests must be fixed and mesh …
alucantonio Jun 23, 2023
e89d6d7
Some renaming.
alucantonio Jun 23, 2023
e81b038
Improved comments and names.
alucantonio Jun 23, 2023
d3e8544
[WIP] Restructuring mesh generation functions.
alucantonio Jun 24, 2023
77afe71
[WIP] Implemented extraction of boundary faces. Cleanup on mesh gener…
alucantonio Jun 24, 2023
1bad64f
Removing test mesh files. Now test meshes are generated from code.
alucantonio Jun 25, 2023
5b0ab39
Fixed function for the generation of 1D meshes.
alucantonio Jun 26, 2023
42e6420
Fixed benchmark and moved some files.
alucantonio Jun 26, 2023
a29ee6a
Updated README.md
alucantonio Jun 26, 2023
867aa74
Minor improvements.
alucantonio Jun 26, 2023
8624166
Fixed test_simplex.
alucantonio Jun 26, 2023
f8c1ca8
[WIP] Rewrited generate_tet_mesh with pygmsh
Smantii Jun 26, 2023
679b7f4
Fixed test_simplicial_complex_3
Smantii Jun 26, 2023
53f57d8
Implemented vectorization of get_dual_volumes and fixed warning issues.
Smantii Jun 26, 2023
02c5e51
[WIP] Started to reformulate test_cochain.py
Smantii Jun 26, 2023
982d762
[WIP] Fixed linting.
Smantii Jun 26, 2023
71a33dd
Improved test_cochain.py
Smantii Jun 27, 2023
8496d9f
Small changes to test_cochain.py
Smantii Jun 27, 2023
97c1a4b
Added and tested compute_B in simplex.py
Smantii Jun 27, 2023
5b726ab
Vectorized circumcenter.py and improved test_circumcenter.py
Smantii Jun 27, 2023
9304d41
Added notebook for fracture of v-notched sample.
alucantonio Jun 28, 2023
7000bac
Improved documentation and variables' name
Smantii Jun 28, 2023
6e138a0
[WIP] Implemented and tested flat_DPP and relative modules to compute…
Smantii Jun 29, 2023
61f7ba2
[WIP] Fixed linear_elasticity notebook.
Smantii Jun 29, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions src/dctkit/apps/linear_elasticity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import numpy as np
import numpy.typing as npt
from dctkit.mesh.simplex import SimplicialComplex
import dctkit.dec.cochain as C
import dctkit.dec.vector as V
from jax import Array
import jax.numpy as jnp
from typing import Tuple


class LinearElasticity():
def __init__(self, S: SimplicialComplex, mu_: float, lambda_: float):
self.S = S
self.mu_ = mu_
self.lambda_ = lambda_

def __get_current_metric_2D(self, node_coords: npt.Array | Array):
Smantii marked this conversation as resolved.
Show resolved Hide resolved
"""Compute the multiarray of shape (n, 2, 2) where n is the number of
2-simplices and any 2x2 matrix is the metric of the corresponding 2-simplex.
"""
dim = self.S.dim
B = self.S.B[dim]
primal_edges = self.S.S[1]
# construct the matrix in which the i-th row corresponds to the vector
# of coordinates of the i-th primal edge
primal_edge_vectors = node_coords[primal_edges[:, 1], :] - \
node_coords[primal_edges[:, 0], :]
# construct the multiarray of shape (n, 3, 3) where any 3x3 matrix represents
# the coordinates of the edge vectors (arranged in rows) belonging to the
# corresponding 2-simplex
primal_edges_per_2_simplex = primal_edge_vectors[B]
# extract the first two rows, i.e. basis vectors, for each 3x3 matrix
basis_vectors = primal_edges_per_2_simplex[:, :-1, :]
metric = basis_vectors @ np.transpose(
basis_vectors, axes=(0, 2, 1))
return metric

def linear_elasticity_residual(self, node_coords: C.CochainP0,
f: C.CochainP2) -> C.CochainP2:
dim = self.S.dim
g = self.__get_current_metric_2D(node_coords=node_coords.coeffs)
g_tensor = V.DiscreteTensorFieldD(S=self.S, coeffs=g)
epsilon = 1/2 * (g_tensor - self.S.metric)
tr_epsilon = jnp.trace(epsilon, axis1=1, axis2=2)
stress = 2*self.mu*epsilon + self.lambda_*tr_epsilon[:, None, None] * \
Smantii marked this conversation as resolved.
Show resolved Hide resolved
np.stack([np.identity(2)]*dim)
stress_integrated = V.flat_DPD(stress)
residual = C.coboundary(C.star(stress_integrated)) + f
return residual

def obj_linear_elasticity(self, node_coords: npt.Array, f: npt.Array, gamma: float,
boundary_values: Tuple[npt.NDArray, npt.NDArray]):
pos, value = boundary_values
node_coords_coch = C.CochainP0(S=self.S, coeffs=node_coords)
f_coch = C.CochainP2(complex=self.S, coeffs=f)
residual = self.linear_elasticity_residual(node_coords_coch, f_coch).coeffs
penalty = np.sum((node_coords[pos] - value)**2)
5 changes: 3 additions & 2 deletions src/dctkit/dec/cochain.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,11 +293,12 @@ def star(c: Cochain) -> Cochain:
coeffs=jnp.empty_like(c.coeffs, dtype=dt.float_dtype))

if c.is_primal:
star_c.coeffs = c.complex.hodge_star[c.dim]*c.coeffs
star_c.coeffs = (c.complex.hodge_star[c.dim]*c.coeffs.T).T
else:
# NOTE: this step only works with well-centered meshes!
assert c.complex.is_well_centered
star_c.coeffs = c.complex.hodge_star_inverse[c.complex.dim - c.dim]*c.coeffs
star_c.coeffs = (
c.complex.hodge_star_inverse[c.complex.dim - c.dim]*c.coeffs.T).T

return star_c

Expand Down
33 changes: 29 additions & 4 deletions src/dctkit/dec/vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,29 @@ def __init__(self, S: spx.SimplicialComplex, is_primal: bool,
self.coeffs = coeffs


class DiscreteTensorField():
def __init__(self, S: spx.SimplicialComplex, is_primal: bool,
coeffs: npt.NDArray | Array):
self.S = S
self.is_primal = is_primal
self.coeffs = coeffs


class DiscreteVectorFieldD(DiscreteVectorField):
Smantii marked this conversation as resolved.
Show resolved Hide resolved
"""Inherited class for dual discrete vector fields."""

def __init__(self, S: spx.SimplicialComplex, coeffs: npt.NDArray | Array):
super().__init__(S, False, coeffs)


def flat_DPD(v: DiscreteVectorFieldD) -> CochainD1:
class DiscreteTensorFieldD(DiscreteTensorField):
"""Inherited class for dual discrete tensor fields."""

def __init__(self, S: spx.SimplicialComplex, coeffs: npt.NDArray | Array):
super().__init__(S, False, coeffs)


def flat_DPD(v: DiscreteVectorFieldD | DiscreteTensorFieldD) -> CochainD1:
"""Implements the flat operator for dual discrete vector fields.

Args:
Expand All @@ -40,7 +55,17 @@ def flat_DPD(v: DiscreteVectorFieldD) -> CochainD1:
dedges = v.S.dual_edges_vectors
flat_matrix = v.S.flat_weights
# multiply weights of each dual edge by the vectors associated to the dual nodes
# belonging to the edge and then perform dot product row-wise with the edge vectors
# of the dual edges (see definition of DPD in Hirani, pag. 54).
coch_coeffs = jnp.sum((v.coeffs @ flat_matrix).T * dedges, axis=1)
# belonging to the edge
weighted_v = v.coeffs @ flat_matrix
weighted_v_T = weighted_v.T
if v.coeffs.ndim == 2:
# vector field case
# perform dot product row-wise with the edge vectors
# of the dual edges (see definition of DPD in Hirani, pag. 54).
coch_coeffs = jnp.einsum("ij, ij -> i", weighted_v_T, dedges)
elif v.coeffs.ndim == 3:
Smantii marked this conversation as resolved.
Show resolved Hide resolved
# tensor field case
# apply each matrix (rows of the multiarray weighted_v_T fixing the first axis)
# to the edge vector of the corresponding dual edge
coch_coeffs = jnp.einsum("ijk, ik -> ij", weighted_v_T, dedges)
return CochainD1(v.S, coch_coeffs)
5 changes: 5 additions & 0 deletions src/dctkit/mesh/simplex.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,11 @@ def get_flat_weights(self):
dual_edges_indices] = np.linalg.norm(diff_circs, axis=1)

self.flat_weights = self.dual_edges_fractions_lengths/self.dual_edges_lengths
# in the case of non-well centered mesh an entry of the flat weights matrix
# can be NaN. In this case, the corresponding dual edge is the null vector,
# hence we shouldn't take in account dot product with it. We then substitute
# any NaN with 0.
self.flat_weights = np.nan_to_num(self.flat_weights)

def get_metric_2D(self):
"""Compute the multiarray of shape (n, 2, 2) where n is the number of
Expand Down
27 changes: 27 additions & 0 deletions tests/test_cochain.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,30 @@ def test_cochain(setup_test):

assert inner_product.dtype == dctkit.float_dtype
assert np.allclose(inner_product, inner_product_true)

# vector-valued cochain test
c0_v_coeffs = np.arange(15).reshape((5, 3))
c1_v_coeffs = np.arange(24).reshape((8, 3))
c0_v = cochain.CochainP0(cpx, c0_v_coeffs)
c1_v = cochain.CochainP1(cpx, c1_v_coeffs)
dc0_v = cochain.coboundary(c0_v)
dc1_v = cochain.coboundary(c1_v)
dc0_v_true = np.array([[3, 3, 3],
[6, 6, 6],
[12, 12, 12],
[6, 6, 6],
[9, 9, 9],
[3, 3, 3],
[6, 6, 6],
[3, 3, 3]], dtype=dctkit.float_dtype)
dc1_v_true = np.array([[6, 7, 8],
[-15, -16, -17],
[18, 19, 20],
[-18, -19, -20]], dtype=dctkit.float_dtype)
assert np.allclose(dc0_v.coeffs, dc0_v_true)
assert np.allclose(dc1_v.coeffs, dc1_v_true)

star_c0_v = cochain.star(c0_v)
star_c0_v_true = 0.125*c0_v_coeffs
star_c0_v_true[-1, :] = np.array([6, 6.5, 7])
assert np.allclose(star_c0_v.coeffs, star_c0_v_true)
16 changes: 12 additions & 4 deletions tests/test_vector.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import numpy as np
import dctkit as dt
from dctkit.mesh import simplex, util
import dctkit.dec.vector as V
import jax.numpy as jnp
Expand All @@ -15,9 +16,16 @@ def test_vector_cochain(setup_test):
S.get_flat_weights()

# test flat operator
v_coeffs = np.ones((S.embedded_dim, S.S[2].shape[0]))
v_coeffs = np.ones((S.embedded_dim, S.S[2].shape[0]), dtype=dt.float_dtype)
T_coeffs = np.ones((S.embedded_dim, S.embedded_dim,
S.S[2].shape[0]), dtype=dt.float_dtype)
v = V.DiscreteVectorFieldD(S, v_coeffs)
c = V.flat_DPD(v)
c_true_coeffs = S.dual_edges_vectors.sum(axis=1)
T = V.DiscreteTensorFieldD(S, T_coeffs)
c_v = V.flat_DPD(v)
c_T = V.flat_DPD(T)
c_v_true_coeffs = S.dual_edges_vectors.sum(axis=1)
c_T_true_coeffs = np.ones((12, 3), dtype=dt.float_dtype)
c_T_true_coeffs = c_v_true_coeffs[:, None]*c_T_true_coeffs

assert jnp.allclose(c.coeffs, c_true_coeffs)
assert jnp.allclose(c_v.coeffs, c_v_true_coeffs)
Smantii marked this conversation as resolved.
Show resolved Hide resolved
assert jnp.allclose(c_T.coeffs, c_T_true_coeffs)
Loading