Skip to content

Commit 158ddd1

Browse files
sanathkeshavsiggmoIshaanDesai
authored
Merge pull request #33 from DataAnalyticsEngineering/FANS-v0.3.0
* Mention dependency on compiler support for OpenMP in README * Rename container user also in fans_dev image to fix broken entrypoint script (#27) * Add python3-dev to fans-ci stage already * Remove clang-format check via GitHub Action (#29) * Bump clang-format version * Formatting * Use a different Action workflow to run clang-format * Remove the Action to run clang-format, as it will go out of sync with the pre-commit run * Bump mirror-clang-format to latest version in the pre-commit-config * Update clang-format config file to also format JSON * Manually specify which extensions to format in the mirror-clang-format hooks in the pre-commit config * Exclude JSON files from clang-format * Exclude JSON files from clang-format * Correctly exclude JSON files from clang-format checking * Rather than excluding json, specifically suggest c++ * Added API to get homogenized stress and homogenized tangent (#31) * worked on getting the algorithmic tangent via finite differences * linear elastic triclinic material model * refactored material properties from type map<string, vector<double>> to type json * J2Plasticity time_step is double not a vector * refactored compute_error to accept different error measure and type * added additional error control when get_homogenized_tangent is called * in get_homogenized_tangent linear materials are handled seperately * added linear thermal triclinic model (#32) * added linear thermal triclinic model similar to linear elastic triclinic model * fixed readme.md indent causing linting error * Update CHANGELOG * Bump version --------- Co-authored-by: Moritz Sigg <[email protected]> Co-authored-by: Moritz Sigg <[email protected]> Co-authored-by: Ishaan Desai <[email protected]>
2 parents 077addf + 58c8f08 commit 158ddd1

25 files changed

+634
-269
lines changed

.github/workflows/clang-format-check.yml

-21
This file was deleted.

.pre-commit-config.yaml

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ repos:
1616
- id: black
1717
# clang-format for C/C++ formatting
1818
- repo: https://github.com/pre-commit/mirrors-clang-format
19-
rev: v8.0.1
19+
rev: v19.1.2
2020
hooks:
2121
- id: clang-format
2222
args: ['--style=file']
2323
exclude: "include/json.hpp"
24+
types_or: [c++]

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# FANS Changelog
22

3+
## v0.3.0
4+
5+
- Added Linear thermal and mechanical triclinic material models https://github.com/DataAnalyticsEngineering/FANS/pull/32
6+
- Added API to get homogenized stress and homogenized tangent https://github.com/DataAnalyticsEngineering/FANS/pull/31
7+
38
## v0.2.0
49

510
- Add integration tests https://github.com/DataAnalyticsEngineering/FANS/pull/20

CMakeLists.txt

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ cmake_minimum_required(VERSION 3.0...3.28)
55
# ##############################################################################
66

77
project(FANS
8-
VERSION 0.2.0
8+
VERSION 0.3.0
99
LANGUAGES C CXX
1010
)
1111

@@ -133,8 +133,8 @@ set_property(TARGET FANS_FANS PROPERTY PUBLIC_HEADER
133133
include/solver.h
134134
include/setup.h
135135

136-
include/material_models/LinearThermalIsotropic.h
137-
include/material_models/LinearElasticIsotropic.h
136+
include/material_models/LinearThermal.h
137+
include/material_models/LinearElastic.h
138138
include/material_models/PseudoPlastic.h
139139
include/material_models/J2Plasticity.h
140140
)

FANS_Dashboard/PlotYoungsModulus.py

+114-53
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,89 @@
11
import numpy as np
22
import plotly.graph_objs as go
3+
import meshio
34

45

5-
def compute_3d_youngs_modulus(C):
6+
def compute_YoungsModulus3D(C_batch):
67
"""
7-
Compute Young's modulus for all directions in 3D.
8+
Compute Young's modulus for all directions in 3D for a batch of stiffness tensors.
89
9-
Parameters:
10-
C : ndarray
11-
Stiffness tensor in Mandel notation.
10+
Args:
11+
C_batch (ndarray): Batch of stiffness tensors in Mandel notation, shape (n, 6, 6).
1212
1313
Returns:
14-
E: ndarray
15-
Young's modulus in all directions.
16-
X, Y, Z: ndarrays
17-
Coordinates for plotting the modulus surface.
14+
tuple: A tuple containing:
15+
- X_batch (ndarray): X-coordinates for plotting the modulus surface, shape (n, n_theta, n_phi).
16+
- Y_batch (ndarray): Y-coordinates for plotting the modulus surface, shape (n, n_theta, n_phi).
17+
- Z_batch (ndarray): Z-coordinates for plotting the modulus surface, shape (n, n_theta, n_phi).
18+
- E_batch (ndarray): Young's modulus in all directions, shape (n, n_theta, n_phi).
1819
"""
19-
20+
n = C_batch.shape[0]
2021
n_theta = 180
2122
n_phi = 360
22-
theta = np.linspace(0, np.pi, n_theta) # Polar angle
23-
phi = np.linspace(0, 2 * np.pi, n_phi) # Azimuthal angle
2423

25-
S = np.linalg.inv(C)
24+
theta = np.linspace(0, np.pi, n_theta)
25+
phi = np.linspace(0, 2 * np.pi, n_phi)
26+
theta_grid, phi_grid = np.meshgrid(theta, phi, indexing="ij")
27+
28+
d_x = np.sin(theta_grid) * np.cos(phi_grid) # Shape (n_theta, n_phi)
29+
d_y = np.sin(theta_grid) * np.sin(phi_grid)
30+
d_z = np.cos(theta_grid)
31+
32+
N = np.stack(
33+
(
34+
d_x**2,
35+
d_y**2,
36+
d_z**2,
37+
np.sqrt(2) * d_x * d_y,
38+
np.sqrt(2) * d_x * d_z,
39+
np.sqrt(2) * d_y * d_z,
40+
),
41+
axis=-1,
42+
) # Shape (n_theta, n_phi, 6)
2643

27-
E = np.zeros((n_theta, n_phi))
44+
N_flat = N.reshape(-1, 6) # Shape (n_points, 6)
2845

29-
for i in range(n_theta):
30-
for j in range(n_phi):
31-
d = np.array(
32-
[
33-
np.sin(theta[i]) * np.cos(phi[j]),
34-
np.sin(theta[i]) * np.sin(phi[j]),
35-
np.cos(theta[i]),
36-
]
37-
)
46+
# Invert stiffness tensors to get compliance tensors
47+
S_batch = np.linalg.inv(C_batch) # Shape (n, 6, 6)
3848

39-
N = np.array(
40-
[
41-
d[0] ** 2,
42-
d[1] ** 2,
43-
d[2] ** 2,
44-
np.sqrt(2.0) * d[0] * d[1],
45-
np.sqrt(2.0) * d[0] * d[2],
46-
np.sqrt(2.0) * d[2] * d[1],
47-
]
48-
)
49+
# Compute E for each tensor in the batch
50+
NSN = np.einsum("pi,nij,pj->np", N_flat, S_batch, N_flat) # Shape (n, n_points)
51+
E_batch = 1.0 / NSN # Shape (n, n_points)
4952

50-
E[i, j] = 1.0 / (N.T @ S @ N)
53+
# Reshape E_batch back to (n, n_theta, n_phi)
54+
E_batch = E_batch.reshape(n, *d_x.shape)
5155

52-
X = E * np.sin(theta)[:, np.newaxis] * np.cos(phi)[np.newaxis, :]
53-
Y = E * np.sin(theta)[:, np.newaxis] * np.sin(phi)[np.newaxis, :]
54-
Z = E * np.cos(theta)[:, np.newaxis]
56+
X_batch = E_batch * d_x # Shape (n, n_theta, n_phi)
57+
Y_batch = E_batch * d_y
58+
Z_batch = E_batch * d_z
5559

56-
return X, Y, Z, E
60+
return X_batch, Y_batch, Z_batch, E_batch
5761

5862

59-
def plot_3d_youngs_modulus_surface(C, title="Young's Modulus Surface"):
63+
def plot_YoungsModulus3D(C, title="Young's Modulus Surface"):
6064
"""
6165
Plot a 3D surface of Young's modulus.
6266
63-
Parameters:
64-
C : ndarray
65-
Stiffness tensor in Mandel notation.
66-
title : str
67-
Title of the plot.
67+
Args:
68+
C (ndarray): Stiffness tensor in Mandel notation. Can be a single tensor of shape (6,6) or a batch of tensors of shape (n,6,6).
69+
title (str): Title of the plot.
6870
71+
Raises:
72+
ValueError: If C is not of shape (6,6) or (1,6,6).
6973
"""
70-
X, Y, Z, E = compute_3d_youngs_modulus(C)
74+
if C.shape == (6, 6):
75+
C_batch = C[np.newaxis, :, :]
76+
elif C.shape == (1, 6, 6):
77+
C_batch = C
78+
else:
79+
raise ValueError(
80+
"C must be either a (6,6) tensor or a batch with one tensor of shape (1,6,6)."
81+
)
82+
83+
X_batch, Y_batch, Z_batch, E_batch = compute_YoungsModulus3D(C_batch)
84+
X, Y, Z, E = X_batch[0], Y_batch[0], Z_batch[0], E_batch[0]
7185

7286
surface = go.Surface(x=X, y=Y, z=Z, surfacecolor=E, colorscale="Viridis")
73-
7487
layout = go.Layout(
7588
title=title,
7689
scene=dict(
@@ -85,14 +98,64 @@ def plot_3d_youngs_modulus_surface(C, title="Young's Modulus Surface"):
8598
fig.show()
8699

87100

101+
def export_YoungsModulus3D_to_vtk(C, prefix="youngs_modulus_surface"):
102+
"""
103+
Export the computed Young's modulus surfaces to VTK files for Paraview visualization.
104+
105+
Args:
106+
C (ndarray): Stiffness tensor in Mandel notation. Can be a single tensor of shape (6,6) or a batch of tensors of shape (n,6,6).
107+
prefix (str): Prefix for the output files.
108+
109+
Returns:
110+
None
111+
"""
112+
X_batch, Y_batch, Z_batch, E_batch = compute_YoungsModulus3D(C)
113+
n, n_theta, n_phi = X_batch.shape
114+
115+
for k in range(n):
116+
points = np.vstack(
117+
(X_batch[k].ravel(), Y_batch[k].ravel(), Z_batch[k].ravel())
118+
).T
119+
cells = [
120+
(
121+
"quad",
122+
np.array(
123+
[
124+
[
125+
i * n_phi + j,
126+
(i + 1) * n_phi + j,
127+
(i + 1) * n_phi + (j + 1),
128+
i * n_phi + (j + 1),
129+
]
130+
for i in range(n_theta - 1)
131+
for j in range(n_phi - 1)
132+
],
133+
dtype=np.int32,
134+
),
135+
)
136+
]
137+
mesh = meshio.Mesh(
138+
points=points,
139+
cells=cells,
140+
point_data={"Youngs_Modulus": E_batch[k].ravel()},
141+
)
142+
filename = f"{prefix}_{k}.vtk"
143+
meshio.write(filename, mesh)
144+
print(f"Exported {filename}")
145+
146+
88147
def demoCubic():
89148
"""
90-
Demonstrates the Young's modulus surface plotting routine for a cubic material (Copper)
149+
Demonstrates the Young's modulus surface plotting routine for a cubic material (Copper).
150+
151+
This function generates the stiffness tensor for a cubic material, specifically copper,
152+
and then plots the 3D Young's modulus surface using the generated tensor.
91153
92-
Returns
93-
-------
94-
None.
154+
Args:
155+
None
95156
157+
Returns:
158+
None
96159
"""
97160
P1 = np.zeros((6, 6))
98161
P1[:3, :3] = 1.0 / 3.0
@@ -104,7 +167,5 @@ def demoCubic():
104167
l1, l2, l3 = 136.67, 46, 150
105168
C = 3 * l1 * P1 + l2 * P2 + l3 * P3
106169

107-
print(C)
108-
109170
# show the 3D Young's modulus plot for copper
110-
plot_3d_youngs_modulus_surface(C, title="Young's Modulus Surface for Copper")
171+
plot_YoungsModulus3D(C, title="Young's Modulus Surface for Copper")

README.md

+12-5
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Fourier Accelerated Nodal Solvers (FANS) is an FFT-based homogenization solver d
1616

1717
FANS has the following dependencies:
1818

19-
- A C++ compiler (e.g. GCC)
19+
- A C++ compiler with OpenMP support (e.g. GCC, or Clang with OpenMP libraries installed)
2020
- CMake (version 3.0 or higher) (+ Unix file utility for creating .deb packages)
2121
- Git (for cloning this repo)
2222
- MPI (mpicc and mpic++)
@@ -178,13 +178,20 @@ FANS requires a JSON input file specifying the problem parameters. Example input
178178
179179
```json
180180
"method": "cg",
181-
"TOL": 1e-10,
182-
"n_it": 100
181+
"error_parameters":{
182+
"measure": "Linfinity",
183+
"type": "absolute",
184+
"tolerance": 1e-10
185+
},
186+
"n_it": 100,
183187
```
184188
185189
- `method`: This indicates the numerical method to be used for solving the system of equations. `cg` stands for the Conjugate Gradient method, and `fp` stands for the Fixed Point method.
186-
- `TOL`: This sets the tolerance level for the solver. It defines the convergence criterion which is based on the L-infinity norm of the nodal finite element residual; the solver iterates until the solution meets this tolerance.
187-
- `n_it`: This specifies the maximum number of iterations allowed for the FANS solver.
190+
- `error_parameters`: This section defines the error parameters for the solver. Error control is applied on the finite element nodal residual of the problem.
191+
- `measure`: Specifies the norm used to measure the error. Options include `Linfinity`, `L1`, or `L2`.
192+
- `type`: Defines the type of error measurement. Options are `absolute` or `relative`.
193+
- `tolerance`: Sets the tolerance level for the solver, defining the convergence criterion based on the chosen error measure. The solver iterates until the solution meets this tolerance.
194+
- `n_it`: Specifies the maximum number of iterations allowed for the FANS solver.
188195
189196
### Macroscale Loading Conditions
190197

docker/Dockerfile

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ RUN apt-get update -qq && apt-get install -y --no-install-recommends \
4040
libeigen3-dev \
4141
libfftw3-dev \
4242
libfftw3-mpi-dev \
43+
# Required for preCICE Micro Manager Python bindings
44+
python3-dev \
4345
# Clean up
4446
&& apt-get clean \
4547
&& apt-get autoremove --purge -y \
@@ -60,11 +62,9 @@ RUN apt-get update -qq && apt-get install -y --no-install-recommends \
6062
time \
6163
htop \
6264
vim \
63-
python3 \
6465
python3-pip \
6566
python3-venv \
6667
python-is-python3 \
67-
python3-dev \
6868
# Clean up
6969
&& apt-get clean \
7070
&& apt-get autoremove --purge -y \

docker/Dockerfile_user_env_entrypoint.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ set -e
88
# USAGE: docker run -e HOST_UID=$(id -u) -e HOST_GID=$(id -g) ...
99
# open issue on this topic: https://github.com/docker/roadmap/issues/398
1010
hostgroup="hostgroup"
11-
container_user="develop"
11+
container_user="fans"
1212

1313
if [ "$(id -u -n)" = "root" ]; then
1414
if [ -n "$HOST_UID" ] && [ -n "$HOST_GID" ]; then

docker/README.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
We provide a set of docker images for different use cases on our [Dockerhub profile](https://hub.docker.com/u/unistuttgartdae):
44

55
- **fans-ci**: Contains the minimum tools to build FANS (including dev packages of dependencies with the required headers), but does not include FANS itself. Meant for a CI workflow.
6-
- **fans-dev**: Based upon fans-ci, but offers a non-root user (`develop`) and handling of UID and GID to not mess up permissions when volume mounting into the container. Meant as an quick to setup build environment for FANS.
6+
- **fans-dev**: Based upon fans-ci, but offers a non-root user (`fans`) and handling of UID and GID to not mess up permissions when volume mounting into the container. Meant as an quick to setup build environment for FANS.
77

88
Both images are built for linux/amd64 and linux/arm64 as well as for the three most recent Ubuntu LTS versions (focal, jammy, noble). The Ubuntu version can be selected through tags, e.g. `fans-dev:focal`; `noble` is equivalent to the `latest` tag. The architecture is selected automatically depending on your host platform.
99

@@ -75,7 +75,7 @@ You can attach VS Code to the newly created container in order to actually work
7575
7676
To attach VS Code you need to install the `Remote Development Extension Pack` and the `Docker` Extension. Then open the Docker menu, right click our newly created `fans-dev` container and select "Start" (if not running already) and then "Attach Visual Studio Code".
7777
78-
After attaching VS Code you unfortunately are user `root` in VS Code due to the way the UID and GID mapping is implemented: The container starts as root, executes the entrypoint script which changes UID and GID and only then drops privileges using `gosu`. VS Code though skips the entrypoint script and thus doesn't switch to the non-root user `develop`. You however can do so manually by typing `gosu develop bash` in your terminal sessions inside VS Code.
78+
After attaching VS Code you unfortunately are user `root` in VS Code due to the way the UID and GID mapping is implemented: The container starts as root, executes the entrypoint script which changes UID and GID and only then drops privileges using `gosu`. VS Code though skips the entrypoint script and thus doesn't switch to the non-root user `fans`. You however can do so manually by typing `gosu fans bash` in your terminal sessions inside VS Code.
7979

8080
For further reading and alternative approaches like a full DevContainer setup have a look at
8181

@@ -87,10 +87,10 @@ For further reading and alternative approaches like a full DevContainer setup ha
8787

8888
By building inside the container, FANS is linked against the container's libs and therefore must run inside the container. After attaching to the container you can then continue to use FANS as described in the main [README](../README.md#usage). Just remember that any input and output files need to visible to the container and thus must lie somewhere inside the mounted volumes.
8989
90-
Special care has to be taken if you need to use FANS within scripts on the host, as Docker's interactive mode (`-i`) is not suitable in this case. Instead you need to use `docker exec`. One basically replaces the original `FANS` call by `docker exec -u develop -w /FANS/test fans-dev [original call]`. For example in conjunction with nohup:
90+
Special care has to be taken if you need to use FANS within scripts on the host, as Docker's interactive mode (`-i`) is not suitable in this case. Instead you need to use `docker exec`. One basically replaces the original `FANS` call by `docker exec -u fans -w /FANS/test fans-dev [original call]`. For example in conjunction with nohup:
9191

9292
```bash
9393
docker start fans-dev
94-
nohup /usr/bin/time -v docker exec -u develop -w /FANS/test fans-dev [original call] &
94+
nohup /usr/bin/time -v docker exec -u fans -w /FANS/test fans-dev [original call] &
9595
docker stop fans-dev
9696
```

include/general.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616

1717
using namespace std;
1818

19+
// JSON
20+
#include <json.hpp>
21+
using nlohmann::json;
22+
using namespace nlohmann;
23+
1924
// Packages
2025
#include "fftw3-mpi.h"
2126
#include "fftw3.h" // this includes the serial fftw as well as mpi header files! See https://fftw.org/doc/MPI-Files-and-Data-Types.html
@@ -49,4 +54,4 @@ inline V *FANS_malloc(size_t n)
4954

5055
#define VERBOSITY 0
5156

52-
//#define EIGEN_RUNTIME_NO_MALLOC
57+
// #define EIGEN_RUNTIME_NO_MALLOC

0 commit comments

Comments
 (0)