Skip to content

Workflow example with linear elasticity

Axel Larsson edited this page Mar 23, 2024 · 10 revisions

TL;DR

  • This example demonstrates how to create component-wise ROM from multiple snapshots of a small FOM.
  • Predictions are then made on a larger FOM configuration.
  • We plot the correlation between relative error, speedup and the number of bases for the ROM.
  • The full code is provided at the end of the page.

Problem formulation

FOM

Above figure: Full Order Model. Prescribed displacements in red. Left boundary is clamped.

We seek to train a component-wise ROM that can accurately predict the deformation field of a linear elastic FOM. The FOM is a lattice structure with a clamped left boundary and a prescribed vertical downwards vertical displacement on the right boundary, see above figure. The FOM lattice is made up of three components: one vertical rod with 512 degrees of freedom (DOFs), one horizontal rod, which is just a rotation of the vertical rod, and one square joint component with 128 DOFs. In total, the FOM lattice consists of 72 vertical rods, 72 horizontal rods and 81 joints, leading to a total FOM dimension of 84,096.

Components

Above figure: FOM components. Dimensions in black text, boundary attributes in blue text.

We use the following script to generate the large FOM configuration file, assuming that the current work directory is the main scaleupROM folder. Here, n is the number of grid sections in each dimension (so the total number of joints is (n+1)^2 and the number of rods of each type is n * (n-1))

n=8
python3 utils/python/linelast_comp.py lattice_cantilever $n $n

This will create the FOM configuration file in the scaleupROM folder. Below are some convenience terminal commands to move the configuration file to the examples/linelast/config and build/examples/linelast/config folders:

# Copy FOM config to source directory
rm examples/linelast/config/linelast.lattice.h5
cp linelast.lattice.h5 examples/linelast/config

# Copy FOM config to build directory
rm build/examples/linelast/config/linelast.lattice.h5
cp linelast.lattice.h5 build/examples/linelast/config

# Remove FOM config file in main directory
rm linelast.lattice.h5

Snapshot generation

The goal of our approach is to "Train small, predict large", so we want to generate the training data on a system that is much smaller than the FOM, but still captures the physical behavior of the larger scale structure. We show two ways to do this: gathering snapshots from a smaller FOM, or gathering snapshots from the individual components themselves.

Parameterized problem with BC rotation

The parameterized problem is shown in the figure below. For a rectangular training model, Dirichlet boundary conditions (corresponding to prescribed displacements) and inhomogenous Neumann boundary conditions (corresponding to external forces) are applied to the boundaries.

ParamProb

Above figure: Parameterized Problem. Prescribed displacements in red. Applied external forces in blue.

The Dirichlet BCs are parameterized as $u = [u_x, u_y]$, where $0 \leq u_x, u_y \leq 10 $. The inhomogenous Neumann BCs are parameterized as sine functions with an offset:

$$ F_i(x,y) = \begin{pmatrix} f_i^x + \alpha_i^x * \sin\left(2\pi \cdot \lambda_i^x \cdot \left(\frac{x}{6.0} + \gamma_i^x \right)\right) \text{if} :p_i^x \geq 0.5 \\ f_i^y + \alpha_i^y * \sin\left(2\pi \cdot \lambda_i^y \cdot \left(\frac{y}{6.0} + \gamma_i^y \right)\right) \text{if} :p_i^y \geq 0.5 \end{pmatrix} $$

$$ \begin{align*} -20 &\leq f_i^x, f_i^y \leq 20 \\ -10 &\leq \alpha_i^x, \alpha_i^y \leq 10 \\ 0.1 &\leq \lambda_i^x, \lambda_i^y \leq 1 \\ 0 &\leq \gamma_i^x, \gamma_i^y \leq 1 \\ 0 &\leq p_i^x, p_i^y \leq 1 \\ \end{align*} $$

In the training data generation, all parameters are sampled with a uniform probability distribution over their interval.

We enrich the training data by changing the sides of the training model where the Dirichlet and Neumann BCs are applied. Four different snapshot generation runs for four different BC setups are done in total, see the below figure.

MultiParamProb

Above figure: Parameterized Problem Permutations. Prescribed displacements in red. Applied external forces in blue. BC setup ID in black.

Small FOM sampling

In the case of the small FOM sampling, our training model is a small portion of the FOM. The Neumann and Dirichlet boundary conditions are applied to the outer boundaries and the inner boundaries have homogenous boundary conditions.

SmallFOM

Above figure: Small FOM for BC setup 0. Note that the inner boundaries doesn't have prescribed boundary conditions or applied forces

We start the sampling by creating a configuration file in a similar way to the FOM. The small FOM sampling can be done on a grid of 1x1 grid section (option: cwtrain_1x1) or on a grid of 2x2 sections (option: cwtrain_2x2). The following code generates four different configuration files, each one corresponding to a different BC setup (see previous section).

# Create configs for componentwise training
python3 utils/python/linelast_cwtrain.py cwtrain_1x1

# Copy to examples and build folders
for i in {0..3}
do
    rm examples/linelast/config/linelast.comp_train$i.h5
    cp linelast.comp_train$i.h5 examples/linelast/config

    rm build/examples/linelast/config/linelast.comp_train$i.h5
    cp linelast.comp_train$i.h5 build/examples/linelast/config

    rm linelast.comp_train$i.h5
done

The next step is the actual sample generation, which is done once for every configuration file. The variable n_samp sets the number of samples per BC setup.

cd ~/build/examples/linelast

# Sample generation
n_samp=100
for i in {0..3}
do
    echo "Training $i"
    ../../bin/main -i linelast.comp_train.yml -f main/mode=sample_generation:mesh/component-wise/global_config="config/linelast.comp_train$i.h5":sample_generation/file_path/prefix="linelast_cwtrain$i":sample_generation/random_sample_generator/number_of_samples=$n_samp
done

Since the sampling is done for different configuration files, which is not standard training procedure, the snapshot file names will not match the name format for training the ROM. Therefore we need to rename the snapshot files:

for i in {0..3}
do
    echo "Rename $i"
    mv linelast_cwtrain$i\_sample_joint2D_snapshot.000000 linelast_cwtrain_sample_joint2D_snapshot$i\.000000
    mv linelast_cwtrain$i\_sample_rod2D_H_snapshot.000000 linelast_cwtrain_sample_rod2D_H_snapshot$i\.000000
    mv linelast_cwtrain$i\_sample_rod2D_V_snapshot.000000 linelast_cwtrain_sample_rod2D_V_snapshot$i\.000000
done

Individual component sampling

For the individual component sampling, no configuration file is used. Instead, we need to generate four different mesh files with shifted boundary attributes, to designate where the Dirichlet and Neumann BCs are applied.

cd ~/build/examples/linelast
# Generate sampling meshes
python3 utils/python/linelast_cwtrain.py cwtrain_mesh build/examples/linelast/meshes

Then the sample generation is run for each component and BC setup.

cd ~/build/examples/linelast
n_samp=600

for i in {0..3}
do
    echo "Training $i"
    ../../bin/main -i linelast.comp_train.yml -f main/mode=sample_generation:sample_generation/file_path/prefix="linelast_cwtrain_joint2D_$i":mesh/filename="meshes/joint2D_$i.mesh":sample_generation/random_sample_generator/number_of_samples=$n_samp:mesh/type=submesh

     ../../bin/main -i linelast.comp_train.yml -f main/mode=sample_generation:sample_generation/file_path/prefix="linelast_cwtrain_rod2D_H_$i":mesh/filename="meshes/rod2D_H_$i.mesh":sample_generation/random_sample_generator/number_of_samples=$n_samp:mesh/type=submesh

     ../../bin/main -i linelast.comp_train.yml -f main/mode=sample_generation:sample_generation/file_path/prefix="linelast_cwtrain_rod2D_V_$i":mesh/filename="meshes/rod2D_V_$i.mesh":sample_generation/random_sample_generator/number_of_samples=$n_samp:mesh/type=submesh

done

Finally, we rename the components to fit the snapshot name format for ROM training.

for i in {0..3}
do
    echo "Rename $i"
    mv linelast_cwtrain_joint2D_$i\_sample_comp0_snapshot.000000 linelast_cwtrain_sample_joint2D_snapshot$i\.000000
    mv linelast_cwtrain_rod2D_H_$i\_sample_comp0_snapshot.000000 linelast_cwtrain_sample_rod2D_H_snapshot$i\.000000
    mv linelast_cwtrain_rod2D_V_$i\_sample_comp0_snapshot.000000 linelast_cwtrain_sample_rod2D_V_snapshot$i\.000000
done

Train ROM

We then train the ROM by combining the different snapshot files generated either by individual component sampling or small FOM sampling. The keyword basis/tags/2/snapshot_format/maximum tells us the maximum number in the snapshot file name for each component (every component corresponds to a tag). In this case we set it to 3, since for every component, we have four snapshot files labeled from 0-3

cd ~/build/examples/linelast

../../bin/main -i linelast.comp_train.yml -f main/mode=train_rom:basis/tags/0/snapshot_format/maximum=3:basis/tags/1/snapshot_format/maximum=3:basis/tags/2/snapshot_format/maximum=3

Large scale prediction

The final step is to assess the accuracy and speedup of the ROM. We do this in the below code by varying the number of ROM basis vectors for each component. Here, n is the number of basis vectors for the joint component and nn = 4*n is the number of basis vectors for the rod components. The resulting plot shows the decrease in relative error and speedup as the dimension of the ROM basis increases.

cd ~/build/examples/linelast
# Create directory for basis scaling.
mkdir basis_scaling

# Run basis scaling
for n in 2 4 6 8 10 12 16 20 24 28 32 40 48 56 64 72 80 88 96 104 112 120 128
do
    nn=$((4*$n))
    echo "Prediction $n"
    # Building ROM
    ../../bin/main -i linelast.comp_train.yml -f main/mode=build_rom:main/use_rom=true:basis/tags/0/number_of_basis=$n:basis/tags/1/number_of_basis=$nn:basis/tags/2/number_of_basis=$nn

    # Single run with larger FOM configuration
    ../../bin/main -i linelast.comp_train.yml -f main/use_rom=true:mesh/component-wise/global_config="config/linelast.lattice.h5":parameterized_problem/name="linelast_disp_lattice":single_run/linelast_disp_lattice/rdisp_f=1.0:basis/tags/0/number_of_basis=$n:basis/tags/1/number_of_basis=$nn:basis/tags/2/number_of_basis=$nn -o "basis_scaling/comparison$n.h5"
done

# Plot results
python3 ../../../utils/python/linelast_cwtrain.py bscale basis_scaling scaling.png

scaling

Full program with small FOM sampling

n=8
python3 utils/python/linelast_comp.py lattice_cantilever $n $n

# Copy FOM config to source directory
rm examples/linelast/config/linelast.lattice.h5
cp linelast.lattice.h5 examples/linelast/config

# Copy FOM config to build directory
rm build/examples/linelast/config/linelast.lattice.h5
cp linelast.lattice.h5 build/examples/linelast/config

# Remove FOM config file in main directory
rm linelast.lattice.h5

# Create configs for componentwise training
python3 utils/python/linelast_cwtrain.py cwtrain_1x1

# Copy to examples and build folders
for i in {0..3}
do
    rm examples/linelast/config/linelast.comp_train$i.h5
    cp linelast.comp_train$i.h5 examples/linelast/config

    rm build/examples/linelast/config/linelast.comp_train$i.h5
    cp linelast.comp_train$i.h5 build/examples/linelast/config

    rm linelast.comp_train$i.h5
done

cd ~/build/examples/linelast

# Sample generation
n_samp=100
for i in {0..3}
do
    echo "Training $i"
    ../../bin/main -i linelast.comp_train.yml -f main/mode=sample_generation:mesh/component-wise/global_config="config/linelast.comp_train$i.h5":sample_generation/file_path/prefix="linelast_cwtrain$i":sample_generation/random_sample_generator/number_of_samples=$n_samp
done

for i in {0..3}
do
    echo "Rename $i"
    mv linelast_cwtrain$i\_sample_joint2D_snapshot.000000 linelast_cwtrain_sample_joint2D_snapshot$i\.000000
    mv linelast_cwtrain$i\_sample_rod2D_H_snapshot.000000 linelast_cwtrain_sample_rod2D_H_snapshot$i\.000000
    mv linelast_cwtrain$i\_sample_rod2D_V_snapshot.000000 linelast_cwtrain_sample_rod2D_V_snapshot$i\.000000
done

../../bin/main -i linelast.comp_train.yml -f main/mode=train_rom:basis/tags/0/snapshot_format/maximum=3:basis/tags/1/snapshot_format/maximum=3:basis/tags/2/snapshot_format/maximum=3

# Create directory for basis scaling.
mkdir basis_scaling

# Run basis scaling
for n in 2 4 6 8 10 12 16 20 24 28 32 40 48 56 64 72 80 88 96 104 112 120 128
do
    nn=$((4*$n))
    echo "Prediction $n"
    # Building ROM
    ../../bin/main -i linelast.comp_train.yml -f main/mode=build_rom:main/use_rom=true:basis/tags/0/number_of_basis=$n:basis/tags/1/number_of_basis=$nn:basis/tags/2/number_of_basis=$nn

    # Single run with larger FOM configuration
    ../../bin/main -i linelast.comp_train.yml -f main/use_rom=true:mesh/component-wise/global_config="config/linelast.lattice.h5":parameterized_problem/name="linelast_disp_lattice":single_run/linelast_disp_lattice/rdisp_f=1.0:basis/tags/0/number_of_basis=$n:basis/tags/1/number_of_basis=$nn:basis/tags/2/number_of_basis=$nn -o "basis_scaling/comparison$n.h5"
done

# Plot results
python3 ../../../utils/python/linelast_cwtrain.py bscale basis_scaling scaling.png