diff --git a/examples/.gitignore b/examples/.gitignore
index f3be36e..fb6ae0a 100644
--- a/examples/.gitignore
+++ b/examples/.gitignore
@@ -1 +1,3 @@
paraview
+hdf5
+numpy*
diff --git a/examples/stresses.py b/examples/stresses.py
index 77e31fa..53151ff 100644
--- a/examples/stresses.py
+++ b/examples/stresses.py
@@ -1,92 +1,119 @@
#!/usr/bin/env python3
# @file
# @section LICENSE
#
# Copyright (©) 2016-2020 EPFL (École Polytechnique Fédérale de Lausanne),
# Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
+import argparse
+import os
+
import numpy as np
import tamaas as tm
-import argparse
-from tamaas.dumpers import UVWDumper as Dumper
+from tamaas.dumpers import H5Dumper as Dumper
+from tamaas.dumpers._helper import hdf5toVTK
parser = argparse.ArgumentParser(
description="Hertzian tractios applied on elastic half-space")
parser.add_argument("radius", type=float, help="Radius of sphere")
parser.add_argument("load", type=float, help="Applied normal force")
parser.add_argument("name", help="Output file name")
+parser.add_argument("--plots", help='Show surface normal stress',
+ action="store_true")
args = parser.parse_args()
# Definition of modeled domain
model_type = tm.model_type.volume_2d
-discretization = [127, 127, 127]
-system_size = [1., 1., 1.]
+discretization = [32, 127, 127]
+system_size = [0.25, 1., 1.]
# Material contants
E = 1. # Young's modulus
nu = 0.3 # Poisson's ratio
E_star = E/(1-nu**2) # Hertz modulus
# Creation of model
model = tm.ModelFactory.createModel(model_type,
system_size,
discretization)
model.E = E
model.nu = nu
# Setup for integral operators
residual = tm.ModelFactory.createResidual(model, 0, 0)
# Coordinates
x = np.linspace(0, system_size[1], discretization[1], endpoint=False)
y = np.linspace(0, system_size[2], discretization[2], endpoint=False)
x, y = np.meshgrid(x, y, indexing='ij')
center = [0.5, 0.5]
r = np.sqrt((x-center[0])**2 + (y-center[1])**2)
+local_size = model.getBoundaryDiscretization()
+local_offset = tm.mpi.local_offset(r.shape)
+local_slice = np.s_[local_offset:local_offset+local_size[0], :]
+
# Sphere
R = args.radius
P = args.load
# Contact area
a = (3*P*R/(4*E_star))**(1/3)
p_0 = 3 * P / (2 * np.pi * a**2)
# Pressure definition
traction = model.getTraction()
-traction[r < a, 2] = p_0 * np.sqrt(1 - (r[r < a] / a)**2)
+hertz_traction = np.zeros(discretization[1:])
+hertz_traction[r < a] = p_0 * np.sqrt(1 - (r[r < a] / a)**2)
+traction[..., 2] = hertz_traction[local_slice]
# Array references
displacement = model.getDisplacement()
stress = residual.getStress()
gradient = residual.getVector()
# Applying operator
boussinesq = model.getIntegralOperator("boussinesq")
boussinesq_gradient = model.getIntegralOperator("boussinesq_gradient")
boussinesq.apply(traction, displacement)
boussinesq_gradient.apply(traction, gradient)
model.applyElasticity(stress, gradient)
# Dumper
dumper_helper = Dumper(args.name, 'stress')
model.addDumper(dumper_helper)
model.dump()
-print("Done")
+
+# Converting HDF dump to VTK
+with tm.mpi.sequential():
+ if tm.mpi.rank() == 0:
+ hdf5toVTK(os.path.join('hdf5', 'stress_0000.h5'), args.name)
+
+if args.plots:
+ import matplotlib.pyplot as plt # noqa
+ fig, ax = plt.subplots(1, 2)
+ fig.suptitle("Rank {}".format(tm.mpi.rank()))
+ ax[0].set_title("Traction")
+ ax[1].set_title("Normal Stress")
+
+ ax[0].imshow(traction[..., 2])
+ ax[1].imshow(stress[0, ..., 2])
+ fig.tight_layout()
+ plt.show()
diff --git a/python/tamaas/dumpers/_helper.py b/python/tamaas/dumpers/_helper.py
index 24cb7ba..66bbbcb 100644
--- a/python/tamaas/dumpers/_helper.py
+++ b/python/tamaas/dumpers/_helper.py
@@ -1,141 +1,141 @@
# -*- mode:python; coding: utf-8 -*-
# @file
# @section LICENSE
#
# Copyright (©) 2016-2020 EPFL (École Polytechnique Fédérale de Lausanne),
# Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
"""
Helper functions for dumpers
"""
from functools import wraps
from collections import defaultdict
import os
import numpy as np
from .. import model_type, type_traits, mpi
__all__ = ["step_dump", "directory_dump"]
def _is_surface_field(field, model):
bn = model.getBoundaryDiscretization()
return list(field.shape[:len(bn)]) == bn
def local_slice(field, model):
n = model.getDiscretization()
bn = model.getBoundaryDiscretization()
gshape = mpi.global_shape(bn)
offsets = np.zeros_like(gshape)
offsets[0] = mpi.local_offset(gshape)
if not _is_surface_field(field, model) and len(n) > len(bn):
gshape = [n[0]] + gshape
offsets = np.concatenate(([0], offsets))
return tuple((slice(offset, offset+size, None)
for offset, size in zip(offsets, field.shape)))
def step_dump(cls):
"""
Decorator for dumper with counter for steps
"""
orig_init = cls.__init__
orig_dump = cls.dump
@wraps(cls.__init__)
def __init__(obj, *args, **kwargs):
orig_init(obj, *args, **kwargs)
obj.count = 0
def postfix(obj):
return "_{:04d}".format(obj.count)
@wraps(cls.dump)
def dump(obj, *args, **kwargs):
orig_dump(obj, *args, **kwargs)
obj.count += 1
cls.__init__ = __init__
cls.dump = dump
cls.postfix = property(postfix)
return cls
def directory_dump(directory=""):
"Decorator for dumper in a directory"
def actual_decorator(cls):
orig_dump = cls.dump
orig_filepath = cls.file_path.fget
@wraps(cls.dump)
def dump(obj, *args, **kwargs):
if not os.path.exists(directory) and mpi.rank() == 0:
os.mkdir(directory)
- if not os.path.isdir(directory):
+ if not os.path.isdir(directory) and mpi.rank() == 0:
raise Exception('{} is not a directory'.format(directory))
orig_dump(obj, *args, **kwargs)
@wraps(cls.file_path.fget)
def file_path(obj):
return os.path.join(directory, orig_filepath(obj))
cls.dump = dump
cls.file_path = property(file_path)
return cls
return actual_decorator
def hdf5toVTK(inpath, outname):
"Convert HDF5 dump of a model to VTK"
import h5py as h5 # noqa
from .. import ModelFactory # noqa
from . import UVWDumper # noqa
type_translate = {
'model_type.basic_1d': model_type.basic_1d,
'model_type.basic_2d': model_type.basic_2d,
'model_type.surface_1d': model_type.surface_1d,
'model_type.surface_2d': model_type.surface_2d,
'model_type.volume_1d': model_type.volume_1d,
'model_type.volume_2d': model_type.volume_2d,
}
with h5.File(inpath, 'r') as h5file:
model_t = h5file.attrs['model_type']
system_size = list(h5file.attrs['system_size'])
n = list(h5file.attrs['discretization'])
model = ModelFactory.createModel(type_translate[model_t],
system_size, n)
fields = []
for field in h5file:
model.registerField(field, h5file[field][:])
fields.append(field)
uvw_dumper = UVWDumper(outname, *fields)
uvw_dumper << model