ParaView

[GooseFEM/ParaView.h, GooseFEM/ParaView.hpp]

Note

This header relies on HDF5 and HighFive as dependencies. To avoid such dependencies in usage without ParaView support, this header is not loaded by default. Therefore

#include <GooseFEM/ParaView.h>

If you wish to use ParaView support without making use of HDF5 and HighFive, you have to define GOOSEFEM_NO_HIGHFIVE before including ParaView.h for the first time:

#define GOOSEFEM_NO_HIGHFIVE
#include <GooseFEM/ParaView.h>

In this case the library does not automatically read the shapes of the datasets. Instead you’ll have to provide them as std::vector<size_t>.

HDF5

TimeSeries

A TimeSeries is constructed from a number of Increments. The consecutive Increments are added to the TimeSeries using push_back. The order in which this is done will define the order of the TimeSeries. Each Increment is constructed from a mesh (using Connectivity and Coordinates) and a number of nodal or cell Attributes.

Consider this example

[examples/ParaView/HDF5/main.cpp]

#include <GooseFEM/GooseFEM.h>
#include <GooseFEM/ParaView.h>

namespace PV = GooseFEM::ParaView::HDF5;

int main()
{
  // define mesh
  GooseFEM::Mesh::Quad4::FineLayer mesh(6,18);

  // extract mesh fields
  xt::xtensor<double,2> coor = mesh.coor();
  xt::xtensor<double,2> conn = mesh.conn();
  xt::xtensor<double,2> disp = xt::zeros<double>(coor.shape());

  // vector definition:
  // provides methods to switch between dofval/nodeval/elemvec, or to manipulate a part of them
  GooseFEM::Vector vector(conn, mesh.dofs());

  // FEM quadrature
  GooseFEM::Element::Quad4::Quadrature elem(vector.AsElement(coor));

  // open output file
  H5Easy::File data("output.h5" , H5Easy::File::Overwrite);

  // initialise ParaView metadata
  PV::TimeSeries xdmf;

  // save mesh to output file
  H5Easy::dump(data, "/coor", coor);
  H5Easy::dump(data, "/conn", conn);

  // define strain history
  xt::xtensor<double,1> gamma = xt::linspace<double>(0, 1, 100);

  // loop over increments
  for (size_t inc = 0; inc < gamma.size(); ++inc)
  {
    // apply fictitious strain
    for (size_t node = 0; node < disp.shape(0); ++node)
      disp(node,0) = gamma(inc) * (coor(node,1) - coor(0,1));

    // compute strain tensor
    xt::xtensor<double,4> Eps = elem.SymGradN_vector(vector.AsElement(disp));
    xt::xtensor<double,1> Eps_xy = xt::view(Eps, xt::all(), 0, 0, 1);

    // store data to output file
    H5Easy::dump(data, "/disp/"   + std::to_string(inc), PV::as3d(disp));
    H5Easy::dump(data, "/eps_xy/" + std::to_string(inc), Eps_xy);

    // add increment to ParaView metadata
    xdmf.push_back(PV::Increment(
      PV::Connectivity(data, "/conn", mesh.getElementType()),
      PV::Coordinates (data, "/coor"),
      {
        PV::Attribute(data, "/disp/"   + std::to_string(inc), "Displacement", PV::AttributeType::Node),
        PV::Attribute(data, "/eps_xy/" + std::to_string(inc), "Eps_xy"      , PV::AttributeType::Cell)
      }
    ));
  }

  // write ParaView metadata
  xdmf.write("output.xdmf");

  return 0;
}

Tip

A displacement vector in must be always 3-d in ParaView, even when the mesh is in 2-d. Use the GooseFEM::ParaView::HDF5::as3d(...) function to convert a matrix of 2-d displacements to a matrix of 3-d displacements.

Note

The Python interface avoids the HDF5 and HighFive dependencies. One therefore has to provide the datasets’ shapes. Consider the following Python example:

[examples/ParaView/HDF5/main.py]

import h5py
import numpy                  as np
import GooseFEM               as gf
import GooseFEM.ParaView.HDF5 as pv

# define mesh
mesh = gf.Mesh.Quad4.FineLayer(6, 18)

# extract mesh fields
coor = mesh.coor();
conn = mesh.conn();
disp = np.zeros(coor.shape)

# vector definition:
# provides methods to switch between dofval/nodeval/elemvec, or to manipulate a part of them
vector = gf.Vector(conn, mesh.dofs())

# FEM quadrature
elem = gf.Element.Quad4.Quadrature(vector.AsElement(coor))

# open output file
data = h5py.File("output.h5", "w")

# initialise ParaView metadata
xdmf = pv.TimeSeries()

# save mesh to output file
data["/coor"] = coor
data["/conn"] = conn

# define strain history
gamma = np.linspace(0, 1, 100);

# loop over increments
for inc in range(len(gamma)):

  # apply fictitious strain
  for node in range(disp.shape[0]):
    disp[node,0] = gamma[inc] * (coor[node,1] - coor[0,1])

  # compute strain tensor
  Eps = elem.SymGradN_vector(vector.AsElement(disp));
  Eps_xy = Eps[:, 0, 0, 1]

  # store data to output file
  data["/disp/"   + str(inc)] = pv.as3d(disp)
  data["/eps_xy/" + str(inc)] = Eps_xy

  # ParaView metadata
  # - initialise Increment
  xdmf_inc = pv.Increment(
    pv.Connectivity(data.filename, "/conn", pv.ElementType.Quadrilateral, conn.shape),
    pv.Coordinates (data.filename, "/coor"                              , coor.shape),
  )
  # - add attributes to Increment
  dataset = "/disp/" + str(inc)
  xdmf_inc.push_back(pv.Attribute(
    data.filename, dataset, "Displacement", pv.AttributeType.Node, data[dataset].shape))
  # - add attributes to Increment
  dataset = "/eps_xy/" + str(inc)
  xdmf_inc.push_back(pv.Attribute(
    data.filename, dataset, "Eps_xy", pv.AttributeType.Cell, data[dataset].shape))
  # - add Increment to TimeSeries
  xdmf.push_back(xdmf_inc)

# write ParaView metadata
xdmf.write("output.xdmf");