Introduction
Introduction Statistics Contact Development Disclaimer Help
tadd hertz-mendlin contact model, still untested - granular - granular dynamics…
git clone git://src.adamsgaard.dk/granular
Log
Files
Refs
README
LICENSE
---
commit 35334244861f0274779e5f5df8936931e8649c49
parent 1530b4bb345ad9e65cdf43acda89c5b5ca48ded6
Author: Anders Damsgaard <[email protected]>
Date: Wed, 21 Apr 2021 21:55:20 +0200
add hertz-mendlin contact model, still untested
Diffstat:
M arrays.c | 15 +++++++++++++--
M arrays.h | 1 +
M grain.c | 35 ++++++++++++++++++++---------…
M grain.h | 4 +++-
M grains.c | 96 ++++++++++++++++++++++++++---…
M grains.h | 1 +
M granular.5 | 4 ++++
M packing.c | 28 ++++++++++++++--------------
M simulation.c | 22 +++++++++++++---------
9 files changed, 155 insertions(+), 51 deletions(-)
---
diff --git a/arrays.c b/arrays.c
t@@ -290,8 +290,7 @@ normalize(const double *in, const int n)
double *out;
check_magnitude(__func__, 1, n);
- if (!(out = calloc(n, sizeof(double))))
- err(1, "%s: out calloc", __func__);
+ out = empty(n);
copy_values(in, out, n);
max_val = max(out, n);
t@@ -339,3 +338,15 @@ dot(const double *a, const double *b, const int n)
return out;
}
+
+double *
+cross(const double a[3], const double b[3])
+{
+ double *out = empty(3);
+
+ out[0] = a[1]*b[2] - a[2]*b[1];
+ out[1] = a[2]*b[0] - a[0]*b[2];
+ out[2] = a[0]*b[1] - a[1]*b[0];
+
+ return out;
+}
diff --git a/arrays.h b/arrays.h
t@@ -54,5 +54,6 @@ double * normalize(const double *in, const int n);
double euclidean_norm(const double *a, const int n);
double euclidean_distance(const double *a, const double *b, const int n);
double dot(const double *a, const double *b, const int n);
+double * cross(const double a[3], const double b[3]);
#endif
diff --git a/grain.c b/grain.c
t@@ -23,7 +23,7 @@ grain_defaults(struct grain *g)
{
int i;
- g->radius = 0.5;
+ g->diameter = 1.0;
for (i = 0; i < 3; i++) {
g->pos[i]
= g->vel[i]
t@@ -46,6 +46,8 @@ grain_defaults(struct grain *g)
g->youngs_modulus = 1e9;
g->poissons_ratio = 0.2;
g->friction_coeff = 0.4;
+ g->damping_n = 0.0;
+ g->damping_t = 0.0;
g->tensile_strength = 1e8;
g->shear_strength = 1e8;
g->fracture_toughness = 1e8;
t@@ -86,7 +88,7 @@ print_padded_nd_uint(FILE *stream, const size_t *arr)
void
grain_print(FILE *stream, const struct grain *g)
{
- fprintf(stream, "%.*g\t", FLOATPREC, g->radius * 2.0);
+ fprintf(stream, "%.*g\t", FLOATPREC, g->diameter);
print_padded_nd_double(stream, g->pos);
print_padded_nd_double(stream, g->vel);
print_padded_nd_double(stream, g->acc);
t@@ -105,6 +107,8 @@ grain_print(FILE *stream, const struct grain *g)
fprintf(stream, "%.*g\t", FLOATPREC, g->youngs_modulus);
fprintf(stream, "%.*g\t", FLOATPREC, g->poissons_ratio);
fprintf(stream, "%.*g\t", FLOATPREC, g->friction_coeff);
+ fprintf(stream, "%.*g\t", FLOATPREC, g->damping_n);
+ fprintf(stream, "%.*g\t", FLOATPREC, g->damping_t);
fprintf(stream, "%.*g\t", FLOATPREC, g->tensile_strength);
fprintf(stream, "%.*g\t", FLOATPREC, g->shear_strength);
fprintf(stream, "%.*g\t", FLOATPREC, g->fracture_toughness);
t@@ -118,13 +122,12 @@ grain_print(FILE *stream, const struct grain *g)
struct grain *
grain_read(char *line)
{
- double diameter;
struct grain *g;
if (!(g = malloc(sizeof(struct grain))))
err(1, "%s: grain malloc", __func__);
- if (sscanf(line, "%lg\t" /* 2.0 * radius */
+ if (sscanf(line, "%lg\t" /* diameter */
"%lg\t%lg\t%lg\t" /* pos */
"%lg\t%lg\t%lg\t" /* vel */
"%lg\t%lg\t%lg\t" /* acc */
t@@ -143,6 +146,8 @@ grain_read(char *line)
"%lg\t" /* youngs_modulus */
"%lg\t" /* poissons_ratio */
"%lg\t" /* friction_coeff */
+ "%lg\t" /* damping_n */
+ "%lg\t" /* damping_t */
"%lg\t" /* tensile_strength */
"%lg\t" /* shear_strength */
"%lg\t" /* fracture_toughness */
t@@ -151,7 +156,7 @@ grain_read(char *line)
"%lg\t%lg\t%lg\t" /* contact_stress */
"%lg\t" /* thermal_energy */
"%d\n", /* color */
- &diameter,
+ &g->diameter,
&g->pos[0], &g->pos[1], &g->pos[2],
&g->vel[0], &g->vel[1], &g->vel[2],
&g->acc[0], &g->acc[1], &g->acc[2],
t@@ -170,6 +175,8 @@ grain_read(char *line)
&g->youngs_modulus,
&g->poissons_ratio,
&g->friction_coeff,
+ &g->damping_n,
+ &g->damping_t,
&g->tensile_strength,
&g->shear_strength,
&g->fracture_toughness,
t@@ -177,11 +184,9 @@ grain_read(char *line)
&g->ncontacts,
&g->contact_stress[0], &g->contact_stress[1], &g->con…
&g->thermal_energy,
- &g->color) != 53)
+ &g->color) != 55)
errx(1, "%s: could not read line: %s", __func__, line);
- g->radius = 0.5 * diameter;
-
if (grain_check_values(g))
errx(1, "%s: invalid values in grain line: %s", __func__, line…
t@@ -194,7 +199,7 @@ grain_check_values(const struct grain *g)
int i;
int status = 0;
- check_float_positive("grain->radius", g->radius, &status);
+ check_float_positive("grain->diameter", g->diameter, &status);
for (i = 0; i < 3; i++) {
check_float("grain->pos", g->pos[i], &status);
t@@ -224,6 +229,12 @@ grain_check_values(const struct grain *g)
check_float_non_negative("grain->friction_coeff",
g->friction_coeff,
&status);
+ check_float_non_negative("grain->damping_n",
+ g->damping_n,
+ &status);
+ check_float_non_negative("grain->damping_t",
+ g->damping_t,
+ &status);
check_float_non_negative("grain->tensile_strength",
g->tensile_strength,
&status);
t@@ -247,7 +258,7 @@ grain_check_values(const struct grain *g)
double
grain_volume(const struct grain *g)
{
- return 4.0 / 3.0 * PI * pow(g->radius, 3.0);
+ return 4.0 / 3.0 * PI * pow(g->diameter / 2.0, 3.0);
}
double
t@@ -259,7 +270,7 @@ grain_mass(const struct grain *g)
double
grain_moment_of_inertia(const struct grain *g)
{
- return 2.0 / 5.0 * grain_mass(g) * pow(g->radius, 2.0);
+ return 2.0 / 5.0 * grain_mass(g) * pow(g->diameter / 2.0, 2.0);
}
void
t@@ -348,7 +359,7 @@ grain_register_contact(struct grain *g, size_t i, size_t j,
}
/* second pass: register as new contact if overlapping */
- if (overlap < 0.0)
+ if (overlap > 0.0)
for (ic = 0; ic <= MAXCONTACTS; ic++) {
if (ic == MAXCONTACTS)
err(1, "%s: contact %zu exceeds MAXCONTACTS (%…
diff --git a/grain.h b/grain.h
t@@ -8,7 +8,7 @@
struct grain {
- double radius;
+ double diameter;
double pos[3];
double vel[3];
double acc[3];
t@@ -27,6 +27,8 @@ struct grain {
double youngs_modulus;
double poissons_ratio;
double friction_coeff;
+ double damping_n;
+ double damping_t;
double tensile_strength;
double shear_strength;
double fracture_toughness;
diff --git a/grains.c b/grains.c
t@@ -1,6 +1,7 @@
#include <stdio.h>
#include <math.h>
#include "grain.h"
+#include "arrays.h"
#define VTK_FLOAT_FMT "%.17g "
t@@ -59,15 +60,7 @@ grains_print_vtk(FILE *stream, const struct grain *grains, …
"\t\t\t<PointData Scalars=\"Diameter [m]\" "
"Vectors=\"Angular position [-]\">\n");
- fprintf(stream,
- "\t\t\t\t<DataArray type=\"Float64\" Name=\"Diameter [m]\" "
- "NumberOfComponents=\"1\" format=\"ascii\">\n");
- for (i = 0; i < ng; i++)
- fprintf(stream, VTK_FLOAT_FMT, grains[i].radius * 2.0);
- fprintf(stream,
- "\n"
- "\t\t\t\t</DataArray>\n");
-
+ VTK_XML_SCALAR(diameter, "Diameter [m]", "Float64", VTK_FLOAT_FMT);
VTK_XML_VECTOR(vel, "Velocity [m/s]", "Float64", VTK_FLOAT_FMT);
VTK_XML_VECTOR(acc, "Acceleration [m/s^2]", "Float64", VTK_FLOAT_FMT);
VTK_XML_VECTOR(acc_lock, "Acceleration lock [-]", "Int64", "%d ");
t@@ -85,6 +78,8 @@ grains_print_vtk(FILE *stream, const struct grain *grains, s…
VTK_XML_SCALAR(youngs_modulus, "Young's modulus [Pa]", "Float64", VTK_…
VTK_XML_SCALAR(poissons_ratio, "Poisson's ratio [-]", "Float64", VTK_F…
VTK_XML_SCALAR(friction_coeff, "Friction coefficient [-]", "Float64", …
+ VTK_XML_SCALAR(damping_n, "Contact-normal damping constant [1/s]", "Fl…
+ VTK_XML_SCALAR(damping_t, "Contact-tangential damping constant [1/s]",…
VTK_XML_SCALAR(tensile_strength, "Tensile strength [Pa]", "Float64", V…
VTK_XML_SCALAR(shear_strength, "Shear strength [Pa]", "Float64", VTK_F…
VTK_XML_SCALAR(fracture_toughness, "Fracture toughness [Pa]", "Float64…
t@@ -109,8 +104,8 @@ grains_minimum_grain_edge(const struct grain *grains, size…
double res = INFINITY;
for (i = 0; i < ng; i++)
- if (res > grains[i].pos[d] - grains[i].radius)
- res = grains[i].pos[d] - grains[i].radius;
+ if (res > grains[i].pos[d] - grains[i].diameter / 2.0)
+ res = grains[i].pos[d] - grains[i].diameter / 2.0;
return res;
}
t@@ -122,8 +117,83 @@ grains_maximum_grain_edge(const struct grain *grains, siz…
double res = -INFINITY;
for (i = 0; i < ng; i++)
- if (res < grains[i].pos[d] + grains[i].radius)
- res = grains[i].pos[d] + grains[i].radius;
+ if (res < grains[i].pos[d] + grains[i].diameter / 2.0)
+ res = grains[i].pos[d] + grains[i].diameter / 2.0;
return res;
}
+
+/* Hertz-Mindlin contact model for compressible spheres */
+void
+grains_interact(struct grain *g_i, struct grain *g_j, int ic)
+{
+ int d;
+ double f_n_ij[3], f_t_ij[3],
+ v_ij[3], v_n_ij[3], v_t_ij[3], v_n_dot_n_ij,
+ delta_ij,
+ n_ij[3], u_t_ij[3],
+ d_i, d_j,
+ r_ij[3], r_ij_norm,
+ m_i, m_j, m_ij,
+ E_ij, nu_ij, mu_ij,
+ gamma_n_ij, gamma_t_ij,
+ k_n_ij, k_t_ij, A_ij,
+ angvel_ij[3], *angvel_cross_r_ij, *f_t_cross_r_ij;
+
+ delta_ij = g_i->contacts[ic].overlap;
+ d_i = g_i->diameter;
+ d_j = g_j->diameter;
+ m_i = grain_mass(g_i);
+ m_j = grain_mass(g_j);
+ m_ij = m_i * m_j / (m_i + m_j);
+ r_ij_norm = euclidean_norm(g_i->contacts[ic].centerdist, 3);
+
+ for (d = 0; d < 3; d++) {
+ r_ij[d] = g_i->contacts[ic].centerdist[d];
+ n_ij[d] = r_ij[d]/r_ij_norm;
+ v_ij[d] = g_i->vel[d] - g_j->vel[d];
+ angvel_ij[d] = g_i->angvel[d] + g_j->angvel[d];
+ }
+
+ v_n_dot_n_ij = dot(v_ij, n_ij, 3);
+ angvel_cross_r_ij = cross(angvel_ij, r_ij);
+
+ for (d = 0; d < 3; d++) {
+ v_n_ij[d] = v_n_dot_n_ij * n_ij[d];
+ v_t_ij[d] = v_ij[d] - v_n_ij[d] - 0.5 * angvel_cross_r_ij[d];
+ }
+
+
+ E_ij = fmin(g_i->youngs_modulus, g_j->youngs_modulus);
+ nu_ij = fmin(g_i->poissons_ratio, g_j->poissons_ratio);
+ mu_ij = fmin(g_i->friction_coeff, g_j->friction_coeff);
+ gamma_n_ij = fmin(g_i->damping_n, g_j->damping_n);
+ gamma_t_ij = fmin(g_i->damping_t, g_j->damping_t);
+
+ k_n_ij = 2.0 / 3.0 * E_ij / (1.0 - pow(nu_ij, 2.0));
+ k_t_ij = 2.0 * E_ij / (1.0 + nu_ij) * (2.0 - nu_ij);
+
+ A_ij = sqrt(delta_ij) * sqrt(d_i * d_j / (2.0 * (d_i + d_j)));
+
+ for (d = 0; d < 3; d++) {
+ f_n_ij[d] = A_ij * (k_n_ij * delta_ij * n_ij[d]
+ - m_ij * gamma_n_ij * …
+ f_t_ij[d] = A_ij * (-k_t_ij * u_t_ij[d]
+ - m_ij * gamma_t_ij * v_t_ij[d]);
+ }
+
+ if (euclidean_norm(f_t_ij, 3) >= euclidean_norm(f_n_ij, 3) * mu_ij)
+ return; /* TODO: limit f_t_ij according to Coulomb criterion */
+
+ f_t_cross_r_ij = cross(f_t_ij, r_ij);
+
+ for (d = 0; d < 3; d++) {
+ g_i->force[d] += f_n_ij[d] + f_t_ij[d];
+ g_j->force[d] -= f_n_ij[d] + f_t_ij[d];
+ g_i->torque[d] += -0.5 * f_t_cross_r_ij[d];
+ g_j->torque[d] -= -0.5 * f_t_cross_r_ij[d];
+ }
+
+ free(angvel_cross_r_ij);
+ free(f_t_cross_r_ij);
+}
diff --git a/grains.h b/grains.h
t@@ -8,5 +8,6 @@ void grains_print_vtk(FILE *stream, const struct grain *grains…
double sim_minimum_grain_edge(const struct grain *grains, size_t ng, int d);
double sim_maximum_grain_edge(const struct grain *grains, size_t ng, int d);
+void grains_interact(struct grain *g_i, struct grain *g_j, int nc);
#endif
diff --git a/granular.5 b/granular.5
t@@ -98,6 +98,10 @@ Poisson's ratio [-]
.It
friction coefficient [-]
.It
+contact-normal damping constant [1/s]
+.It
+contact-tangential damping constant [1/s]
+.It
tensile strength [Pa]
.It
shear strength [Pa]
diff --git a/packing.c b/packing.c
t@@ -10,18 +10,18 @@
size_t
rectangular_packing(struct grain **grains,
size_t n[3],
- double radius_min, double radius_max,
+ double diameter_min, double diameter_max,
double (*gsd)(double min, double max),
double padding_factor,
double origo[3])
{
size_t i, j, k, l, ng, N = 0;
- double dx_padding = radius_max * 2.0 * padding_factor;
- double dx = radius_max * 2.0 + dx_padding;
+ double dx_padding = diameter_max * padding_factor;
+ double dx = diameter_max + dx_padding;
- if (radius_max < radius_min)
- errx(1, "%s: radius_max (%g) is smaller than radius_min (%g)",
- __func__, radius_max, radius_min);
+ if (diameter_max < diameter_min)
+ errx(1, "%s: diameter_max (%g) is smaller than diameter_min (%…
+ __func__, diameter_max, diameter_min);
if (!(*grains = calloc(n[0] * n[1] * n[2], sizeof(struct grain))))
err(1, "%s: grains calloc", __func__);
t@@ -32,7 +32,7 @@ rectangular_packing(struct grain **grains,
N++;
ng = idx3(i, j, k, n[0], n[1]);
grain_defaults(&(*grains)[ng]);
- (*grains)[ng].radius = gsd(radius_min, radius_…
+ (*grains)[ng].diameter = gsd(diameter_min, dia…
(*grains)[ng].pos[0] = i * dx + 0.5 * dx + ori…
(*grains)[ng].pos[1] = j * dx + 0.5 * dx + ori…
(*grains)[ng].pos[2] = k * dx + 0.5 * dx + ori…
t@@ -51,19 +51,19 @@ rectangular_packing(struct grain **grains,
size_t
triangular_packing(struct grain **grains,
size_t n[3],
- double radius_min, double radius_max,
+ double diameter_min, double diameter_max,
double (*gsd)(double min, double max),
double padding_factor,
double origo[3])
{
size_t i, j, k, l, ng, N = 0;
- double dx_padding = radius_max * 2.0 * padding_factor;
- double dx = radius_max * 2.0 + dx_padding;
+ double dx_padding = diameter_max * padding_factor;
+ double dx = diameter_max + dx_padding;
double dy = dx * sin(PI/3.0);
- if (radius_max < radius_min)
- errx(1, "%s: radius_max (%g) is smaller than radius_min (%g)",
- __func__, radius_max, radius_min);
+ if (diameter_max < diameter_min)
+ errx(1, "%s: diameter_max (%g) is smaller than diameter_min (%…
+ __func__, diameter_max, diameter_min);
if (!(*grains = calloc(n[0] * n[1] * n[2], sizeof(struct grain))))
err(1, "%s: grains calloc", __func__);
t@@ -74,7 +74,7 @@ triangular_packing(struct grain **grains,
N++;
ng = idx3(i, j, k, n[0], n[1]);
grain_defaults(&(*grains)[ng]);
- (*grains)[ng].radius = gsd(radius_min, radius_…
+ (*grains)[ng].diameter = gsd(diameter_min, dia…
(*grains)[ng].pos[0] = i * dx + 0.5 * dx + ori…
if (j % 2 == 0)
(*grains)[ng].pos[0] += 0.5 * dx;
diff --git a/simulation.c b/simulation.c
t@@ -97,8 +97,8 @@ sim_grain_pair_overlap(struct simulation *sim, size_t i, siz…
for (d = 0; d < 3; d++)
pos[d] = sim->grains[i].pos[d] - sim->grains[j].pos[d];
- overlap = euclidean_norm(pos, 3)
- - (sim->grains[i].radius + sim->grains[j].radius);
+ overlap = (sim->grains[i].diameter + sim->grains[j].diameter) / 2.0
+ - euclidean_norm(pos, 3);
return overlap;
}
t@@ -118,8 +118,8 @@ sim_check_add_contact(struct simulation *sim, size_t i, si…
centerdistinv[d] = sim->grains[j].pos[d] - sim->grains[i].pos[…
}
- overlap = euclidean_norm(centerdist, 3)
- - (sim->grains[i].radius + sim->grains[j].radius);
+ overlap = 0.5*(sim->grains[i].diameter + sim->grains[j].diameter)
+ - euclidean_norm(centerdist, 3);
if (sim->grains[i].enabled)
grain_register_contact(&sim->grains[i], j, i, centerdist, over…
t@@ -134,9 +134,10 @@ sim_detect_contacts(struct simulation *sim)
size_t i, j;
for (i = 0; i < sim->ng; i++)
- for (j = 0; j < sim->ng; j++)
- if (i < j)
- sim_check_add_contact(sim, i, j);
+ if (sim->grains[i].enabled)
+ for (j = 0; j < sim->ng; j++)
+ if (i < j)
+ sim_check_add_contact(sim, i, j);
}
void
t@@ -147,8 +148,11 @@ sim_resolve_interactions(struct simulation *sim)
for (i = 0; i < sim->ng; i++)
for (ic = 0; ic < MAXCONTACTS; ic++)
- if (sim->grains[i].contacts[ic].active)
- grain_interact(i);
+ if (sim->grains[i].contacts[ic].active &&
+ i < sim->grains[i].contacts[ic].j)
+ grains_interact(&sim->grains[i],
+ &sim->grains[sim->grains[i].co…
+ ic);
}
void
You are viewing proxied material from mx1.adamsgaard.dk. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.