Giter VIP home page Giter VIP logo

asimov-contact's People

Contributors

chrisrichardson avatar garth-wells avatar jorgensd avatar sarahro avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

asimov-contact's Issues

Nanoindentation Simulation: Nitsche Method and Model Compatibility

Hello, I have one last query:

I'm using the repository to simulate a nanoindentation test on the cell wall of wood (S2 layer), where I incorporate a hygroviscoelastic constitutive model for the wood material and a linear model for the indented tip.

In relation to this, I have two questions:

  1. Is there any work related to the Nitsche method of this repository in large deformations?

  2. The constitutive model I am using is formulated for large deformations. Therefore, to incorporate it into the repository, I would need to linearize it to work with small deformations. My question at this point is: do they know of any method to achieve compatibility in communication between a material formulated for large deformations and a contact model that operates in small deformations?

Any other comments related to the work I am doing are welcome, as well as any limitations they may identify.

Design better data structure to map cells on one surface to closest cells on the opposite surface

Currently we have

  • _map_0_to_1_facet, _map_1_to_0_facet: adjacency lists mapping facets to facets.
  • _map_0_to_1, _map_1_to_0: xtensors of shape (num_facets, num_quadrature_points, 2). For each facet and each quadrature point on that facet it contains the index of the closest cell at that quadrature point and the local index of the facet.

Related to this: Facets are currently saved as vector of facet indices (_facet_0, _facet_1) and as cell-facet pairs (_cell_facet_pairs_0, _cell_facet_pairs_1). Are both needed?

bug in ray-tracing

  • illustrated by running python3 demo_nitsche_unbiased.py --problem=3 --3D --time_steps=5 --raytracing
    and removing the following lines in demo_nitsche_unbiased.py:
         if args.radius > 0.8 / args.time_steps:
                args.radius = 0.8 / args.time_steps
  • problem can be fixed by adjusting search radius (is this sufficient as a fix?)
  • caused by the following kind of situation: the red facet on the bottom surface is matched with the 4 red facets on the top surface
    facet_map
  • This situation is related to the fix for non-convergence introduced in PR #118, possibly better fix needed

Reconsider input types to kernels

In the current code, the input types of the assembly kernels are closely related to the kernel signature in DOLFINx, i.e.

contact_kernel_fn unbiased_rhs
        = [=](std::vector<std::vector<PetscScalar>>& b, const double* c,
              const double* w, const double* coordinate_dofs,
              const int* entity_local_index, const std::size_t num_links)

The only difference here is that we do not have quadrature permutations as input, and instead we send in number of connected cells (num_links). However, using double* for input requires us to convert data back to 2D structures, which seems unnecessary and adds extra operations.

@garth-wells what do you think? as we are not aiming to put these kernels inside the DOLFINx framework, would it be more sensible to use xtl::span for c and w, and and xt::xtensor<double, 2>& for coordinate_dofs, as this is what we need to compute jacobians.

Strong Dirichlet BC

  • Lifting for standard variational form (UFL)
  • Lifting for contact kernels ("Corner case": one side of a corner is contact surface, other part has prescribed displacement)

Remove `max_links`

Max_links was a way to give the kernel information about how many cells a single facet can be connected to.
We should rather let max_links=num_quadrature_points_per_facet and send in additional information to the assemblers as to which matrix each quadrature points relates to.

This would massively simplify the packing of test functions (and their corresponding gradients), and can be computed whenever we create the distance_map (which will be at packing).

Implementation Query for Viscoelastic Linear Model (Bleyer)

Dear all,

Currently, I am implementing a viscoelastic linear model (Bleyer) which utilizes a mixed functional space. To gradually integrate it, I am solely focusing on creating the mixed space W and working with its vector space W.sub(0).

Upon using W.sub(0), I encounter the following error:
ValueError: Contact kernel not supported for spaces with value size!=1

Additionally, I would like to inquire if there is a version of the demos without kernel optimization. I understand they are written in C++, which hinders my access to details such as those involved in kernel generation.

I have provided a reproducible demo_nitsche_unbiased example for your review.

model (Bleyer): https://comet-fenics.readthedocs.io/en/latest/demo/viscoelasticity/linear_viscoelasticity.html

Para que el código se muestre correctamente en Markdown, necesitas envolverlo en bloques de código, que se indican con tres comillas invertidas (```). También puedes especificar el lenguaje de programación para un resaltado de sintaxis adecuado. Aquí está tu código con el formato correcto:

import argparse
import sys

import numpy as np
import ufl
from dolfinx import default_scalar_type, log
from dolfinx.common import timed, Timer, TimingType, list_timings, timing
from dolfinx.fem import (Constant, dirichletbc, form, Function, Expression, FunctionSpace,
                         VectorFunctionSpace, locate_dofs_topological)
from dolfinx.fem.petsc import create_vector, assemble_vector, assemble_matrix, set_bc, apply_lifting
from dolfinx.graph import adjacencylist
from dolfinx.io import XDMFFile, VTXWriter
from dolfinx.mesh import locate_entities_boundary, GhostMode, meshtags
from mpi4py import MPI
from petsc4py.PETSc import InsertMode, ScatterMode  # type: ignore

from dolfinx_contact.helpers import (epsilon, lame_parameters, sigma_func,
                                     weak_dirichlet)
from dolfinx_contact.meshing import (convert_mesh, create_box_mesh_2D,
                                     create_box_mesh_3D,
                                     create_circle_circle_mesh,
                                     create_circle_plane_mesh,
                                     create_cylinder_cylinder_mesh,
                                     create_sphere_plane_mesh)
from dolfinx_contact.general_contact.contact_problem import ContactProblem, FrictionLaw
from dolfinx_contact.parallel_mesh_ghosting import create_contact_mesh
from dolfinx_contact.helpers import rigid_motions_nullspace_subdomains
from dolfinx_contact.newton_solver import NewtonSolver
from dolfinx_contact.cpp import ContactMode
import os
from typing import Union
from dolfinx import cpp

if __name__ == "__main__":
    desc = "Nitsche's method for two elastic bodies using custom assemblers"
    parser = argparse.ArgumentParser(description=desc,
                                     formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument("--theta", default=1., type=float, dest="theta",
                        help="Theta parameter for Nitsche, 1 symmetric, -1 skew symmetric, 0 Penalty-like",
                        choices=[1., -1., 0.])
    parser.add_argument("--gamma", default=10, type=float, dest="gamma",
                        help="Coercivity/Stabilization parameter for Nitsche condition")
    parser.add_argument("--quadrature", default=5, type=int, dest="q_degree",
                        help="Quadrature degree used for contact integrals")
    parser.add_argument("--problem", default=1, type=int, dest="problem",
                        help="Which problem to solve: 1. Flat surfaces, 2. One curved surface, 3. Two curved surfaces",
                        choices=[1, 2, 3])
    parser.add_argument("--order", default=1, type=int, dest="order",
                        help="Order of mesh geometry", choices=[1, 2, 3])
    _3D = parser.add_mutually_exclusive_group(required=False)
    _3D.add_argument('--3D', dest='threed', action='store_true',
                     help="Use 3D mesh", default=False)
    
    
    _timing = parser.add_mutually_exclusive_group(required=False)
    _timing.add_argument('--timing', dest='timing', action='store_true',
                         help="List timings", default=False)
    _ksp = parser.add_mutually_exclusive_group(required=False)
    _ksp.add_argument('--ksp-view', dest='ksp', action='store_true',
                      help="List ksp options", default=False)
    _simplex = parser.add_mutually_exclusive_group(required=False)
    _simplex.add_argument('--simplex', dest='simplex', action='store_true',
                          help="Use triangle/tet mesh", default=False)
    _strain = parser.add_mutually_exclusive_group(required=False)
    _strain.add_argument('--strain', dest='plane_strain', action='store_true',
                         help="Use plane strain formulation", default=False)
    parser.add_argument("--E", default=1e3, type=np.float64, dest="E",
                        help="Youngs modulus of material")
    parser.add_argument("--nu", default=0.1, type=np.float64, dest="nu", help="Poisson's ratio")
    parser.add_argument("--disp", default=0.2, type=np.float64, dest="disp",
                        help="Displacement BC in negative y direction")
    parser.add_argument("--radius", default=0.5, type=np.float64, dest="radius",
                        help="Search radius for ray-tracing")
    parser.add_argument("--load_steps", default=4, type=np.int32, dest="nload_steps",
                        help="Number of steps for gradual loading")
    parser.add_argument("--res", default=0.1, type=np.float64, dest="res",
                        help="Mesh resolution")
    parser.add_argument("--friction", default=0.0, type=np.float64, dest="fric",
                        help="Friction coefficient for Tresca friction")
    parser.add_argument("--outfile", type=str, default=None, required=False,
                        help="File for appending results", dest="outfile")
    _lifting = parser.add_mutually_exclusive_group(required=False)
    _lifting.add_argument('--lifting', dest='lifting', action='store_true',
                          help="Apply lifting (strong enforcement of Dirichlet condition",
                          default=False)
    _raytracing = parser.add_mutually_exclusive_group(required=False)
    _raytracing.add_argument('--raytracing', dest='raytracing', action='store_true',
                             help="Use raytracing for contact search.",
                             default=False)
    _coulomb = parser.add_mutually_exclusive_group(required=False)
    _coulomb.add_argument('--coulomb', dest='coulomb', action='store_true',
                          help="Use coulomb friction kernel. This requires --friction=[nonzero float value].",
                          default=False)

    # Parse input arguments or set to defualt values
    args = parser.parse_args()
    # Current formulation uses bilateral contact
    threed = args.threed
    problem = args.problem
    nload_steps = args.nload_steps
    simplex = args.simplex
    mesh_dir = "code_utils/tutoriales/results/meshes"
    if not os.path.exists(mesh_dir):
        os.makedirs(mesh_dir)
    triangle_ext = {1: "", 2: "6", 3: "10"}
    tetra_ext = {1: "", 2: "10", 3: "20"}
    hex_ext = {1: "", 2: "27"}
    quad_ext = {1: "", 2: "9", 3: "16"}
    line_ext = {1: "", 2: "3", 3: "4"}
    if args.order > 2:
        raise NotImplementedError("More work in DOLFINx (SubMesh) required for this to work.")
    # Load mesh and create identifier functions for the top (Displacement condition)
    # and the bottom (contact condition)

    if threed:
        displacement = np.array([[0, 0, -args.disp], [0, 0, 0]])
        if problem == 1:
            outname = "code_utils/tutoriales/results/tu8/problem2_3D_simplex" if simplex else "code_utils/tutoriales/results/tu8/problem2_3D_hex"
            fname = f"{mesh_dir}/sphere"
            create_sphere_plane_mesh(filename=f"{fname}.msh", order=args.order, res=args.res)
            convert_mesh(fname, fname, gdim=3)
            with XDMFFile(MPI.COMM_WORLD, f"{fname}.xdmf", "r") as xdmf:
                mesh = xdmf.read_mesh()
                domain_marker = xdmf.read_meshtags(mesh, name="cell_marker")
                tdim = mesh.topology.dim
                mesh.topology.create_connectivity(tdim - 1, tdim)
                facet_marker = xdmf.read_meshtags(mesh, name="facet_marker")
            dirichlet_bdy_1 = 2
            contact_bdy_1 = 1
            contact_bdy_2 = 8
            dirichlet_bdy_2 = 7

    else:
        displacement = np.array([[0, -args.disp], [0, 0]])
        if problem == 1:
            outname = "code_utils/tutoriales/results/tu8/problem2_2D_simplex" if simplex else "code_utils/tutoriales/results/tu8/problem2_2D_quads"
            fname = f"{mesh_dir}/problem2_2D_simplex" if simplex else f"{mesh_dir}/problem2_2D_quads"
            create_circle_plane_mesh(filename=f"{fname}.msh", quads=not simplex,
                                     res=args.res, order=args.order, r=0.3, gap=0.1, height=0.1, length=1.0)
            convert_mesh(fname, f"{fname}.xdmf", gdim=2)

            with XDMFFile(MPI.COMM_WORLD, f"{fname}.xdmf", "r") as xdmf:
                mesh = xdmf.read_mesh()
                domain_marker = xdmf.read_meshtags(mesh, name="cell_marker")
                tdim = mesh.topology.dim
                mesh.topology.create_connectivity(tdim - 1, tdim)
                facet_marker = xdmf.read_meshtags(mesh, name="facet_marker")
            dirichlet_bdy_1 = 8
            contact_bdy_1 = 10
            contact_bdy_2 = 6
            dirichlet_bdy_2 = 4

    if mesh.comm.size > 1:
        mesh, facet_marker, domain_marker = create_contact_mesh(
            mesh, facet_marker, domain_marker, [contact_bdy_1, contact_bdy_2], 2.0)

    ncells = mesh.topology.index_map(tdim).size_local
    indices = np.array(range(ncells), dtype=np.int32)
    values = mesh.comm.rank * np.ones(ncells, dtype=np.int32)
    process_marker = meshtags(mesh, tdim, indices, values)
    process_marker.name = "process_marker"
    domain_marker.name = "cell_marker"
    facet_marker.name = "facet_marker"
    with XDMFFile(mesh.comm, f"{mesh_dir}/test.xdmf", "w") as xdmf:
        xdmf.write_mesh(mesh)
        xdmf.write_meshtags(domain_marker, mesh.geometry)
        xdmf.write_meshtags(facet_marker, mesh.geometry)

    # Solver options
    ksp_tol = 1e-10
    newton_tol = 1e-7
    newton_options = {"relaxation_parameter": 1,
                      "atol": newton_tol,
                      "rtol": newton_tol,
                      "convergence_criterion": "residual",
                      "max_it": 200,
                      "error_on_nonconvergence": True}
    # petsc_options = {"ksp_type": "preonly", "pc_type": "lu"}
    petsc_options = {
        "matptap_via": "scalable",
        "ksp_type": "cg",
        "ksp_rtol": ksp_tol,
        "ksp_atol": ksp_tol,
        "pc_type": "gamg",
        "pc_mg_levels": 3,
        "pc_mg_cycles": 1,   # 1 is v, 2 is w
        "mg_levels_ksp_type": "chebyshev",
        "mg_levels_pc_type": "jacobi",
        "pc_gamg_type": "agg",
        "pc_gamg_coarse_eq_limit": 1000,
        "pc_gamg_agg_nsmooths": 1,
        "pc_gamg_threshold": 0.015,
        "pc_gamg_square_graph": 2,
        "pc_gamg_reuse_interpolation": True,
        "ksp_norm_type": "unpreconditioned"
    }
    # Pack mesh data for Nitsche solver
    dirichlet_vals = [dirichlet_bdy_1, dirichlet_bdy_2]
    contact = [(1, 0), (0, 1)]
    data = np.array([contact_bdy_1, contact_bdy_2], dtype=np.int32)
    offsets = np.array([0, 2], dtype=np.int32)
    surfaces = adjacencylist(data, offsets)

    # Function, TestFunction, TrialFunction and measures
    V = ufl.VectorElement("P", mesh.ufl_cell(), 1)
    Q = ufl.TensorElement("DG", mesh.ufl_cell(), 1)    # , shape = (3,3)
    W = FunctionSpace(mesh, ufl.MixedElement([V, Q]))

    s = Function(W)
    (u, epsv) = ufl.split(s)
    s_old = Function(W)
    (du, epsv_old) = ufl.split(s_old)
    s_ = ufl.TestFunction(W)
    (v, epsv_) = ufl.split(s_)
    s_t = ufl.TrialFunction(W)  
    (w, epsv_t) = ufl.split(s_t)

    # V = VectorFunctionSpace(mesh, ("Lagrange", args.order))
    # u = Function(V)
    # du = Function(V)
    # v = ufl.TestFunction(V)
    # w = ufl.TrialFunction(V)
    dx = ufl.Measure("dx", domain=mesh, subdomain_data=domain_marker)
    ds = ufl.Measure("ds", domain=mesh, subdomain_data=facet_marker)

    # Compute lame parameters
    E = args.E
    nu = args.nu
    mu_func, lambda_func = lame_parameters(args.plane_strain)
    mu = mu_func(E, nu)
    lmbda = lambda_func(E, nu)
    sigma = sigma_func(mu, lmbda)

    # Create variational form without contact contributions
    F = ufl.inner(sigma(u), epsilon(v)) * dx

    # Nitsche parameters
    gamma = args.gamma
    theta = args.theta
    gdim = mesh.geometry.dim
    bcs = []
    bc_fns = []
    for k in range(displacement.shape[0]):
        d = displacement[k, :]
        tag = dirichlet_vals[k]
        g = Constant(mesh, default_scalar_type(tuple(d[i] for i in range(gdim))))
        bc_fns.append(g)

        def weak_dirichlet(F: ufl.Form, u: Function,
                        f: Union[Function, Constant], sigma, gamma, theta, ds):
            V = s.function_space
            # V = W.sub(0)  # I also use this
            # v = F.arguments()[0]

            mesh = V.mesh
            V2 = FunctionSpace(mesh, ("DG", 0))
            tdim = mesh.topology.dim
            ncells = mesh.topology.index_map(tdim).size_local
            h = Function(V2)
            h_vals = cpp.mesh.h(mesh._cpp_object, mesh.topology.dim, np.arange(0, ncells, dtype=np.int32))
            h.x.array[:ncells] = h_vals[:]
            n = ufl.FacetNormal(mesh)
            
            F += - ufl.inner(sigma(u) * n, v) * ds\
                - theta * ufl.inner(sigma(v) * n, u - f) * \
                ds + gamma / h * ufl.inner(u - f, v) * ds
            return F

        F = weak_dirichlet(F, u, g, sigma, E * gamma * args.order**2, theta, ds(tag))

    F = ufl.replace(F, {u: u + du})
    J = ufl.derivative(F, du, w)

    # compiler options to improve performance
    cffi_options = ["-Ofast", "-march=native"]
    jit_options = {"cffi_extra_compile_args": cffi_options,
                   "cffi_libraries": ["m"]}
    # compiled forms for rhs and tangen system
    F_compiled = form(F, jit_options=jit_options)
    J_compiled = form(J, jit_options=jit_options)

    log.set_log_level(log.LogLevel.WARNING)
    num_newton_its = np.zeros(nload_steps, dtype=int)
    num_krylov_its = np.zeros(nload_steps, dtype=int)
    newton_time = np.zeros(nload_steps, dtype=np.float64)

    solver_outfile = args.outfile if args.ksp else None

    V0 = FunctionSpace(mesh, ("DG", 0))
    mu0 = Function(V0)
    lmbda0 = Function(V0)
    fric = Function(V0)
    mu0.interpolate(lambda x: np.full((1, x.shape[1]), mu))
    lmbda0.interpolate(lambda x: np.full((1, x.shape[1]), lmbda))
    fric.interpolate(lambda x: np.full((1, x.shape[1]), args.fric))

    if args.raytracing:
        search_mode = [ContactMode.Raytracing for _ in range(len(contact))]
    else:
        search_mode = [ContactMode.ClosestPoint for _ in range(len(contact))]

    # create contact solver
    contact_problem = ContactProblem([facet_marker], surfaces, contact, mesh, args.q_degree, search_mode)
    if args.coulomb:
        friction_law = FrictionLaw.Coulomb
    else:
        friction_law = FrictionLaw.Tresca
    contact_problem.generate_contact_data(friction_law, W.sub(0), {"u": u, "du": du, "mu": mu0,
                                                            "lambda": lmbda0, "fric": fric},
                                          E * gamma * args.order**2, theta)

    # define functions for newton solver
    def compute_coefficients(x, coeffs):
        size_local = V.dofmap.index_map.size_local
        bs = V.dofmap.index_map_bs
        du.x.array[:size_local * bs] = x.array_r[:size_local * bs]
        du.x.scatter_forward()
        contact_problem.update_contact_data(du)

    @timed("~Contact: Assemble residual")
    def compute_residual(x, b, coeffs):
        b.zeroEntries()
        b.ghostUpdate(addv=InsertMode.INSERT,
                      mode=ScatterMode.FORWARD)
        with Timer("~~Contact: Contact contributions (in assemble vector)"):
            contact_problem.assemble_vector(b, V)
        with Timer("~~Contact: Standard contributions (in assemble vector)"):
            assemble_vector(b, F_compiled)

        # Apply boundary condition
        if len(bcs) > 0:
            apply_lifting(
                b, [J_compiled], bcs=[bcs], x0=[x], scale=-1.0)
        b.ghostUpdate(addv=InsertMode.ADD,
                      mode=ScatterMode.REVERSE)
        if len(bcs) > 0:
            set_bc(b, bcs, x, -1.0)

    @timed("~Contact: Assemble matrix")
    def compute_jacobian_matrix(x, a_mat, coeffs):
        a_mat.zeroEntries()
        with Timer("~~Contact: Contact contributions (in assemble matrix)"):
            contact_problem.assemble_matrix(a_mat, V)
        with Timer("~~Contact: Standard contributions (in assemble matrix)"):
            assemble_matrix(a_mat, J_compiled, bcs=bcs)
        a_mat.assemble()

    # create vector and matrix
    a_mat = contact_problem.create_matrix(J_compiled)
    b = create_vector(F_compiled)

    # Set up snes solver for nonlinear solver
    newton_solver = NewtonSolver(mesh.comm, a_mat, b, contact_problem.coeffs)
    # Set matrix-vector computations
    newton_solver.set_residual(compute_residual)
    newton_solver.set_jacobian(compute_jacobian_matrix)
    newton_solver.set_coefficients(compute_coefficients)

    # Set rigid motion nullspace
    null_space = rigid_motions_nullspace_subdomains(V, domain_marker, np.unique(
        domain_marker.values), num_domains=len(np.unique(domain_marker.values)))
    newton_solver.A.setNearNullSpace(null_space)

    # Set Newton solver options
    newton_solver.set_newton_options(newton_options)

    # Set Krylov solver options
    newton_solver.set_krylov_options(petsc_options)

    # initialise vtx writer
    u.name = "u"
    vtx = VTXWriter(mesh.comm, f"{outname}_{nload_steps}_step.bp", [u], "bp4")
    vtx.write(0)
    for i in range(nload_steps):
        for k, g in enumerate(bc_fns):
            if len(bcs) > 0:
                g.value[:] = displacement[k, :] / nload_steps
            else:
                g.value[:] = (i + 1) * displacement[k, :] / nload_steps
        timing_str = f"~Contact: {i+1} Newton Solver"
        with Timer(timing_str):
            n, converged = newton_solver.solve(du, write_solution=True)
        num_newton_its[i] = n

        du.x.scatter_forward()
        u.x.array[:] += du.x.array[:]
        contact_problem.update_contact_detection(u)
        a_mat = contact_problem.create_matrix(J_compiled)
        a_mat.setNearNullSpace(null_space)
        newton_solver.set_petsc_matrix(a_mat)
        du.x.array[:] = 0.1 * du.x.array[:]
        contact_problem.update_contact_data(du)
        vtx.write(i + 1)
    vtx.close()

Errors while running asimov-contact demos

Hello, recently I was using the asimov-contact repository without any issues, but due to an error on my computer, I had to perform a fresh installation. Unfortunately, it's not working as before, and it's generating the following errors when running the demos.

code demo_nitsche_unbiased
image

This is running on WSL in an environment. Below are the installation steps in case I've made any mistakes.

Create and activate the conda environment

sudo apt-get update
conda create -n contact
conda activate contact

Install packages

conda install -c conda-forge fenics-dolfinx mpich pyvista
conda install -c conda-forge pybind11
conda install -c conda-forge cmake
conda install -c conda-forge pocl
conda install -c conda-forge oclgrind
conda install -c conda-forge intel-compute-runtime
conda install -c conda-forge beignet
conda install -c conda-forge ocl-icd-system
conda install -c conda-forge scipy
pip install pytest
conda update pip
conda update setuptools
python3 -m pip install numpy
pip install gmsh

Clone and build the git repository

git clone https://github.com/Wells-Group/asimov-contact.git
cd asimov-contact/cpp
mkdir build
cd build

Set environment variables for the compiler

export CC=/usr/bin/gcc
export CXX=/usr/bin/g++

Configure with cmake and specify the conda installation prefix

cmake -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX ..

Build and install

make -j4
make install

Install the Python package

cd ../../python
python3 -m pip install -v .

Verify the installation

conda list

Test packing of test function and its derivative at a given set of points

We can test the packing of derivatives of test functions by:

  • Creating a mesh that is a single element
  • Create a function space on this element
  • Create N number of functions, the ith function is 1 in its ith entry, 0 everywhere else.
  • Use expression to evaluate grad(u_i) at quadrature points.
  • Compare these values to the packed test functions.

Improve sort linked cells

  • in pack_test_fn and pack_ny, make sorting of linked cells more transparent
  • move sorting in separate function

Change notation of surfaces

Use Surface_A and Surface_B as opposed to 0 and 1, as this is always varying from paper to paper (some start with 1 as index), and also makes the code harder to read.

self contact

Dear all,

I'm currently working on a self-contact problem (see figure). To do this, I've constructed this problem where C1 and C2 are the surfaces that come into contact and u=a, u=0 are the Dirichlet conditions applied to the surfaces opposite the contact surface.

The code runs, but does not converge. I'm confused because I'm not sure I fully understood the concept of self-contact in this article (http://dx.doi.org/10.1016/j.cma.2017.07.015). In fact, self-contact is dealt with implicitly.

Looking at the code in the demos, I noticed that there are always two distinct geometries, but I'm wondering how to exploit this for a self-contact test.

Your help in understanding how to implement this problem would be a real stepping stone for me.

Thanks in advance.

self

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.