Note

This notebook can be downloaded here: RVETest.ipynb

RVE Test

%load_ext autoreload
%autoreload 2
import compmod2 as cp2
import argiope as ag
import pandas as pd
import numpy as np
from string import Template
import inspect, os, local_settings, scipy
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d as mpl3d
import matplotlib.colors as colors
import matplotlib as mpl
%matplotlib nbagg
# USEFUL FUNCTIONS
def create_dir(path):
    try:
        os.mkdir(path)
    except:
        pass
# SETTINGS
workdir   = "_workdir/"
outputdir = "outputs/"
label   = "RVE"
create_dir(workdir)
create_dir(workdir + outputdir)

Model settings

%%time
shape = np.array([4, 4, 4])
Ne = shape.prod()

def element_map(mesh):
    mesh.elements.loc[:, ("type", "solver", "") ] = "C3D8R"
    return mesh


def material_map(mesh):
    mesh.elements.materials = ["mat{0}".format(i) for i in mesh.elements.index]
    return mesh

dist = cp2.distributions.Weibull(k = 1., l = 1.e-2)
xt, x = dist.discretize(Ne, xmax = 1.)
np.random.shuffle(x)
E = 210.e3
nu = .3
materials = [ag.materials.ElasticPerfectlyPlastic(
                                 label = "mat{0}".format(i+1),
                                 young_modulus = E,
                                 poisson_ratio = nu,
                                 yield_stress = x[i] * E)
                                 for i in range(Ne)]

steps = [cp2.models.RVEStep(name = "loading1",
                            cx = ("disp", .1),
                            field_output_frequency = 100),
         cp2.models.RVEStep(name = "unloading1",
                            cx = ("force", 0.),
                            field_output_frequency = 100),
         cp2.models.RVEStep(name = "loading2",
                            cx = ("disp", .1),
                            field_output_frequency = 100),]

sample = cp2.models.RVESample(shape = shape,
                              element_map = element_map,
                              material_map = material_map)
model = cp2.models.RVETest(label = label,
                           parts = {"sample":sample},
                           steps = steps,
                           materials = materials,
                           solver = "abaqus",
                           solver_path = local_settings.ABAQUS_PATH,
                           workdir = workdir,
                           verbose = True)
CPU times: user 10.5 ms, sys: 0 ns, total: 10.5 ms
Wall time: 10.5 ms

Solving

if True:
    model.write_input()
    #model.run_simulation()
    model.postproc()
    model.save(workdir + label + ".pckl")
#### POST-PROCESSING "RVE" USING POST-PROCESSOR "ABAQUS"
     Abaqus License Manager checked out the following license(s):
     "cae" release 6.13 from flex2-symme.univ-savoie.fr
     <7 out of 9 licenses remain available>.
  => POST-PROCESSED RVE: DURATION = 1.68s >

Post-Processing

model = ag.utils.load(workdir + label + ".pckl")
data = model.data["history"]
data.head()
areas dimensions disp energies ... strains stress time volume
A1 A2 A3 L1 L2 L3 U1 U2 U3 We ... E22 E33 LE11 LE22 LE33 S11 S22 S33 t V
frame
0 1.000000 1.000000 1.000000 1.000 1.000000 1.000000 0.000 0.000000 0.000000 0.000000 ... 0.000000 0.000000 0.000000 0.000000 0.000000 NaN NaN NaN 0.00 1.000000
1 0.999375 1.000687 1.000687 1.001 0.999688 0.999687 0.001 -0.000312 -0.000313 0.095873 ... -0.000312 -0.000313 0.001000 -0.000312 -0.000313 NaN NaN NaN 0.01 1.000374
2 0.998707 1.001349 1.001354 1.002 0.999356 0.999351 0.002 -0.000644 -0.000649 0.353291 ... -0.000644 -0.000649 0.001998 -0.000645 -0.000650 NaN NaN NaN 0.02 1.000704
3 0.998000 1.001992 1.002001 1.003 0.999004 0.998995 0.003 -0.000996 -0.001005 0.732349 ... -0.000996 -0.001005 0.002996 -0.000996 -0.001005 NaN NaN NaN 0.03 1.000994
4 0.997261 1.002617 1.002631 1.004 0.998636 0.998623 0.004 -0.001364 -0.001377 1.199574 ... -0.001364 -0.001377 0.003992 -0.001365 -0.001378 NaN NaN NaN 0.04 1.001250

5 rows × 33 columns

plt.figure()
for i in data.step.s.unique():
    step = data[data.step.s == i]
    plt.plot(step.strains.LE11, step.stress.S11, label = "step {0}".format(i))
plt.xlabel("Log. strain, $E$")
plt.ylabel("Cauchy Stress, $\sigma$")
plt.grid()
plt.legend(loc = "best")
plt.show()
<IPython.core.display.Javascript object>
plt.figure()
for i in data.step.s.unique():
    step = data[data.step.s == i]
    plt.plot(step.strains.LE11, step.energies.Wp, label = "step {0}".format(i))
plt.xlabel("Log. strain, $E$")
plt.ylabel("Plastic Dissipated Energy, $W_p$")
plt.grid()
plt.legend(loc = "best")
plt.show()

RVE plot

step_label = "loading1"
frame = -1
field_label = "LE"
component_label = "v11"

# FIELDS MANAGEMENT
fdata = model.parts["sample"].mesh.fields_metadata()
fields = model.parts["sample"].mesh.fields
F_id = fdata[(fdata.step_label == step_label) & (fdata.label == field_label)].sort_values("frame").index[frame]
F = fields[F_id].data[component_label]
fdata
frame frame_value label part position step_label step_num
0 0 0 EE ISAMPLE element loading1 0
1 0 0 LE ISAMPLE element loading1 0
2 0 0 PE ISAMPLE element loading1 0
3 0 0 S ISAMPLE element loading1 0
4 0 0 U ISAMPLE node loading1 0
5 1 1 EE ISAMPLE element loading1 0
6 1 1 LE ISAMPLE element loading1 0
7 1 1 PE ISAMPLE element loading1 0
8 1 1 S ISAMPLE element loading1 0
9 1 1 U ISAMPLE node loading1 0
10 0 0 EE ISAMPLE element unloading1 1
11 0 0 LE ISAMPLE element unloading1 1
12 0 0 PE ISAMPLE element unloading1 1
13 0 0 S ISAMPLE element unloading1 1
14 0 0 U ISAMPLE node unloading1 1
15 1 1 EE ISAMPLE element unloading1 1
16 1 1 LE ISAMPLE element unloading1 1
17 1 1 PE ISAMPLE element unloading1 1
18 1 1 S ISAMPLE element unloading1 1
19 1 1 U ISAMPLE node unloading1 1
20 0 0 EE ISAMPLE element loading2 2
21 0 0 LE ISAMPLE element loading2 2
22 0 0 PE ISAMPLE element loading2 2
23 0 0 S ISAMPLE element loading2 2
24 0 0 U ISAMPLE node loading2 2
25 1 1 EE ISAMPLE element loading2 2
26 1 1 LE ISAMPLE element loading2 2
27 1 1 PE ISAMPLE element loading2 2
28 1 1 S ISAMPLE element loading2 2
29 1 1 U ISAMPLE node loading2 2
# PATCHES
vertices, emap = model.get_Poly3DCollection(deformed = True,
                                            step_label = step_label,
                                            frame = frame,
                                            displacement_factor = 1.)

collection = mpl3d.art3d.Poly3DCollection(vertices)
collection.set_array(F.loc[emap] * 100)
collection.set_linewidth(.5)
collection.set_edgecolor("black")
collection.set_cmap(mpl.cm.jet)
collection.set_clim(0., 20.)

# FIGURE
fig = plt.figure()
ax1 = fig.add_subplot(1, 1, 1, projection='3d', aspect = "equal")
ax1.add_collection3d(collection)
#ax1.axis("off")
ax1.set_xlabel(r'loading direction, $\vec x$ axis')
ax1.set_ylabel(r'$\vec y$ axis')
ax1.set_zlabel(r'$\vec z$ axis')
offset = .05
ax1.set_xlim3d(-offset, 1. + offset)
ax1.set_ylim3d(-offset, 1. + offset)
ax1.set_zlim3d(-offset, 1. + offset)
ax1.set_xticks([0, 1])
ax1.set_yticks([0, 1])
ax1.set_zticks([0, 1])
cbar = plt.colorbar(collection)
cbar.set_label(r"True Strain Tensor, $LE_{11}$ [$\%$]")
cbar.set_ticks(np.linspace(0., 20, 6))
plt.savefig("model.pdf")
plt.show()
<IPython.core.display.Javascript object>

Periodic BC checking

U_id = fdata[(fdata.step_label == step_label) & (fdata.label == "U")].sort_values("frame").index[frame]
U = fields[U_id].data
nodes = model.parts["sample"].mesh.nodes

def check_direction(direction = "x"):
    """
    Calculates the difference of the displacements on 2 opposite faces that are normal to a given direction.
    """
    F0 = nodes[(nodes.sets.control == False) & (nodes.coords[direction] == 0.)].coords.sort_values(["x", "y", "z"])
    F1 = nodes[(nodes.sets.control == False) & (nodes.coords[direction] == 1.)].coords.sort_values(["x", "y", "z"])
    U0 = U.loc[F0.index].reset_index()
    U1 = U.loc[F1.index].reset_index()
    return U1 - U0

check_direction("x").describe()
node v1 v2 v3
count 25.0 2.500000e+01 25.0 25.0
mean 4.0 1.000000e-01 0.0 0.0
std 0.0 2.665862e-09 0.0 0.0
min 4.0 1.000000e-01 0.0 0.0
25% 4.0 1.000000e-01 0.0 0.0
50% 4.0 1.000000e-01 0.0 0.0
75% 4.0 1.000000e-01 0.0 0.0
max 4.0 1.000000e-01 0.0 0.0
check_direction("y").describe()
node v1 v2 v3
count 25.0 25.0 2.500000e+01 25.0
mean 20.0 0.0 -4.163784e-02 0.0
std 0.0 0.0 7.457459e-10 0.0
min 20.0 0.0 -4.163784e-02 0.0
25% 20.0 0.0 -4.163784e-02 0.0
50% 20.0 0.0 -4.163784e-02 0.0
75% 20.0 0.0 -4.163784e-02 0.0
max 20.0 0.0 -4.163784e-02 0.0
check_direction("z").describe()
node v1 v2 v3
count 25.0 25.0 25.0 2.500000e+01
mean 100.0 0.0 0.0 -4.791145e-02
std 0.0 0.0 0.0 1.103872e-09
min 100.0 0.0 0.0 -4.791145e-02
25% 100.0 0.0 0.0 -4.791145e-02
50% 100.0 0.0 0.0 -4.791145e-02
75% 100.0 0.0 0.0 -4.791145e-02
max 100.0 0.0 0.0 -4.791145e-02

The boundary conditions are perfectly matched.