NEML2 2.0.0
Loading...
Searching...
No Matches
Model composition

Problem definition

Recall that the complete model for simulating the projectile trajectory is

\begin{align} \dot{\boldsymbol{x}} & = \boldsymbol{v}, \label{1} \\ \dot{\boldsymbol{v}} & = \boldsymbol{a} = \boldsymbol{g} - \mu \boldsymbol{v}, \label{2} \\ \mathbf{r} = & = \begin{Bmatrix} \tilde{\boldsymbol{x}} - \boldsymbol{x}_n - \left(t - t_n\right) \dot{\boldsymbol{x}} \\ \tilde{\boldsymbol{v}} - \boldsymbol{v}_n - \left(t - t_n\right) \dot{\boldsymbol{v}} \\ \end{Bmatrix}, \label{3} \\ \begin{Bmatrix} \boldsymbol{x} \\ \boldsymbol{v} \end{Bmatrix} & = \mathop{\mathrm{root}}\limits_{\tilde{\boldsymbol{x}}, \tilde{\boldsymbol{v}}} \left( \mathbf{r} \right), \label{4} \end{align}

subject to appropriate initial conditions \(\boldsymbol{x}_0\) and \(\boldsymbol{v}_0\)

Among these equations:

  • \(\eqref{1}\) and \(\eqref{3}\) can be defined using VecBackwardEulerTimeIntegration.
  • \(\eqref{2}\) is the custom model ProjectileAcceleration which we have implemented in previous tutorials.
  • \(\eqref{4}\) is the ImplicitUpdate.

This tutorial demonstrates that our custom model ProjectileAcceleration can be composed with other existing, predefined NEML2 models.

Input file

The following input file composes the constitutive model for a single-step update for 5 projectiles each with a different dynamic viscosity, i.e., the shape of "mu" is \((5;)\).

[Models]
[eq2]
type = ProjectileAcceleration
velocity = 'state/v'
acceleration = 'state/a'
gravitational_acceleration = 'g'
dynamic_viscosity = 'mu'
[]
[eq3a]
type = VecBackwardEulerTimeIntegration
variable = 'state/x'
rate = 'state/v'
[]
[eq3b]
type = VecBackwardEulerTimeIntegration
variable = 'state/v'
rate = 'state/a'
[]
[eq3]
type = ComposedModel
models = 'eq3a eq3b'
[]
[system]
type = ComposedModel
models = 'eq2 eq3'
[]
[eq4]
type = ImplicitUpdate
implicit_model = 'system'
solver = 'newton'
[]
[]

Lauching the projectiles

To obtain the entire trajectory, the constitutive model need to be recursively integrated. As discussed in a previous tutorial, a transient driver should be used to perform the recursive constitutive update. Conveniently, in this example, since we are dealing with an autonomous system (i.e., no external "forces" are needed to drive the constitutive update), the vanilla TransientDriver can be used:

[Drivers]
[driver]
type = TransientDriver
model = 'eq4'
prescribed_time = 'times'
ic_Vec_names = 'state/x state/v'
ic_Vec_values = 'x0 v0'
save_as = 'result.pt'
[]
[]

The five projectiles are launched from the same position (the origin) but with two different lauching velocities. Note how broadcasting is used to simultaneously simulate the trajectories of all 10 combinations.

#include "neml2/drivers/Driver.h"
int
main()
{
using namespace neml2;
set_default_dtype(kFloat64);
auto factory = load_input("input.i");
auto driver = factory->get_driver("driver");
driver->run();
}
Definition DiagnosticsInterface.cxx:30

Output @list-output:ex1

The following Python script plots the trajectories loaded from "result.pt" written by the driver.

import torch
from matplotlib import pyplot as plt
# Load the result
res = torch.jit.load("result.pt")
O = dict(res.output.named_buffers())
# Plot
fig, ax = plt.subplots(1, 2, figsize=(10, 5))
nstep = 100
nvel = 2 # two launching velocities
nproj = 5 # five projectiles (with different dynamic viscosity)
for i in range(nvel):
for j in range(nproj):
x = [O["{}.state/x".format(n)][i, j, 0].item() for n in range(1, nstep)]
y = [O["{}.state/x".format(n)][i, j, 1].item() for n in range(1, nstep)]
ax[i].plot(x, y, "--")
ax[i].set_xlabel("x")
ax[i].set_ylabel("y")
ax[i].grid()
fig.tight_layout()
fig.savefig("trajectories.svg")
Projectile trajectories