Skip to content

Commit

Permalink
add tubes for rendering streamlines (#1239)
Browse files Browse the repository at this point in the history
* add tubes for rendering streamlines
* add seed population options
  • Loading branch information
nicolemarsaglia authored Apr 29, 2024
1 parent 6e6a70b commit e3e4732
Show file tree
Hide file tree
Showing 33 changed files with 2,207 additions and 128 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ and this project aspires to adhere to [Semantic Versioning](https://semver.org/s
- Added parameters to control HDF5 compression options to the Relay Extract.
- Added check to make sure all domain IDs are unique
- Added a `vtk` extract that saves each mesh domain to a legacy vtk file grouped, with all domain data grouped by a `.visit` file.
- Added WarpX Streamline filter that uses charged particles.
- Added seed population options for particle advection: point, point list, line, and box

### Changed
- Changed the Data Binning filter to accept a `reduction_field` parameter (instead of `var`), and similarly the axis parameters to take `field` (instead of `var`). The `var` style parameters are still accepted, but deprecated and will be removed in a future release.
- Changed the Streamline and WarpXStreamline filters to apply the VTK-m Tube filter to their outputs, allowing for the results to be rendered.

## [0.9.2] - Released 2023-06-30
### Preferred dependency versions for [email protected]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// Copyright (c) Lawrence Livermore National Security, LLC and other Ascent
// Project developers. See top-level LICENSE AND COPYRIGHT files for dates and
// other details. No copyright assignment is required to contribute to Ascent.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//

//-----------------------------------------------------------------------------
///
/// file: ascent_tutorial_cpp_utils.hpp
///
//-----------------------------------------------------------------------------

#ifndef ASCENT_TUTORIAL_CPP_UTILS_H
#define ASCENT_TUTORIAL_CPP_UTILS_H

#include <iostream>
#include "conduit_blueprint.hpp"

#include <math.h>

using namespace conduit;

const float64 PI_VALUE = 3.14159265359;

// --------------------------------------------------------------------------//
void
tutorial_tets_example(Node &mesh)
{
mesh.reset();

//
// (create example tet mesh from blueprint example 2)
//
// Create a 3D mesh defined on an explicit set of points,
// composed of two tets, with two element associated fields
// (`var1` and `var2`)
//

// create an explicit coordinate set
double X[5] = { -1.0, 0.0, 0.0, 0.0, 1.0 };
double Y[5] = { 0.0, -1.0, 0.0, 1.0, 0.0 };
double Z[5] = { 0.0, 0.0, 1.0, 0.0, 0.0 };
mesh["coordsets/coords/type"] = "explicit";
mesh["coordsets/coords/values/x"].set(X, 5);
mesh["coordsets/coords/values/y"].set(Y, 5);
mesh["coordsets/coords/values/z"].set(Z, 5);


// add an unstructured topology
mesh["topologies/mesh/type"] = "unstructured";
// reference the coordinate set by name
mesh["topologies/mesh/coordset"] = "coords";
// set topology shape type
mesh["topologies/mesh/elements/shape"] = "tet";
// add a connectivity array for the tets
int64 connectivity[8] = { 0, 1, 3, 2, 4, 3, 1, 2 };
mesh["topologies/mesh/elements/connectivity"].set(connectivity, 8);

const int num_elements = 2;
float var1_vals[num_elements] = { 0, 1 };
float var2_vals[num_elements] = { 1, 0 };

// create a field named var1
mesh["fields/var1/association"] = "element";
mesh["fields/var1/topology"] = "mesh";
mesh["fields/var1/values"].set(var1_vals, 2);

// create a field named var2
mesh["fields/var2/association"] = "element";
mesh["fields/var2/topology"] = "mesh";
mesh["fields/var2/values"].set(var2_vals, 2);

// make sure the mesh we created conforms to the blueprint
Node verify_info;
if(!blueprint::mesh::verify(mesh, verify_info))
{
std::cout << "Mesh Verify failed!" << std::endl;
std::cout << verify_info.to_yaml() << std::endl;
}
}

// --------------------------------------------------------------------------//
void
tutorial_gyre_example(float64 time_value, Node &mesh)
{
mesh.reset();
int xy_dims = 40;
int z_dims = 2;

conduit::blueprint::mesh::examples::braid("hexs",
xy_dims,
xy_dims,
z_dims,
mesh);

mesh["state/time"] = time_value;
Node &field = mesh["fields/gyre"];
field["association"] = "vertex";
field["topology"] = "mesh";
field["values"].set(DataType::float64(xy_dims*xy_dims*z_dims));

Node &vec_field = mesh["fields/gyre_vel"];
vec_field["association"] = "vertex";
vec_field["topology"] = "mesh";
vec_field["values/u"].set(DataType::float64(xy_dims*xy_dims*z_dims));
vec_field["values/v"].set(DataType::float64(xy_dims*xy_dims*z_dims));
vec_field["values/w"].set(DataType::float64(xy_dims*xy_dims*z_dims));

float64 *values_ptr = field["values"].value();
float64 *u_values_ptr = vec_field["values/u"].value();
float64 *v_values_ptr = vec_field["values/v"].value();
float64 *w_values_ptr = vec_field["values/w"].value();

float64 e = 0.25;
float64 A = 0.1;
float64 w = (2.0 * PI_VALUE) / 10.0;
float64 a_t = e * sin(w * time_value);
float64 b_t = 1.0 - 2 * e * sin(w * time_value);
// print("e: " + str(e) + " A " + str(A) + " w " + str(w) + " a_t " + str(a_t) + " b_t " + str(b_t))
// print(b_t)
// print(w)
int idx = 0;
for (int z=0; z < z_dims; z++)
{
for (int y=0; y < xy_dims; y++)
{
// scale y to 0-1
float64 y_n = float64(y)/float64(xy_dims);
float64 y_t = sin(PI_VALUE * y_n);
for (int x=0; x < xy_dims; x++)
{
// scale x to 0-1
float64 x_f = float(x)/ (float(xy_dims) * .5);
float64 f_t = a_t * x_f * x_f + b_t * x_f;
// print(f_t)
float64 value = A * sin(PI_VALUE * f_t) * y_t;
float64 u = -PI_VALUE * A * sin(PI_VALUE * f_t) * cos(PI_VALUE * y_n);
float64 df_dx = 2.0 * a_t + b_t;
// print("df_dx " + str(df_dx))
float64 v = PI_VALUE * A * cos(PI_VALUE * f_t) * sin(PI_VALUE * y_n) * df_dx;
values_ptr[idx] = sqrt(u * u + v * v);
u_values_ptr[idx] = u;
v_values_ptr[idx] = v;
w_values_ptr[idx] = 0;
// values[idx] = u * u + v * v
// values[idx] = value
// print("u " + str(u) + " v " + str(v) + " mag " + str(math.sqrt(u * u + v * v)))
idx++;
}
}
}

//print(values)
}

#endif
23 changes: 23 additions & 0 deletions src/docs/sphinx/Actions/Examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,17 @@ Resulting image:

.. image:: examples/tout_trigger_extract_inline100.png

An example of the streamline filter using point list seed placement
--------------------------------------------------------------------

YAML actions:

.. literalinclude:: examples/tout_streamline_point_list100.yaml

Resulting image:

.. image:: examples/tout_streamline_point_list100.png

An example of the interconnecting pipelines.
---------------------------------------------

Expand Down Expand Up @@ -755,6 +766,18 @@ Resulting image:

.. image:: examples/tout_cell_gradient_mag_radial100.png

An example of using the streamline filter and associated tube parameters to produce a rendering.
--------------------------------------------------------------------------------------------------

YAML actions:

.. literalinclude:: examples/tout_streamline_render100.yaml

Resulting image:

.. image:: examples/tout_streamline_render100.png


An example of using the xray extract.
--------------------------------------

Expand Down
186 changes: 186 additions & 0 deletions src/docs/sphinx/Actions/Pipelines.rst
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,192 @@ values are removed from the data set.

An example of creating a iso-volume of values between 5.0 and 10.0.

Particle Advection
~~~~~~~~~~~~~~~~
The particle advection filter distributes some number of weightless particles over a user-specified vector field (``field``) and, given some advection distance (``step_size``), advects them for some number of advection steps (``num_steps``).

.. code-block:: c++

conduit::Node pipelines;
// pipeline 1
pipelines["pl1/f1/type"] = "particle_advection";
//required params
conduit::Node &params = pipelines["pl1/f1/params"];
params["field"] = "vel"; // name of the vector field
params["step_size"] = 0.01; // advection step size
params["num_steps"] = 100; // number of advection steps

Users also need to specify how to generate seed placements (``seeds``).
The seed placements can be an individual point (``point``), a list of points (``point_list``), a line (``line``), or a box (``box``).
The seed placement type will determine the necessary parameters:

- ``point`` requires a ``location`` as an [x,y,z] list of doubles.
- ``point_list`` requires a ``location`` as an [x0,y0,z0,...,xn,yn,zn] list of doubles.
- ``line`` requires a ``start`` and ``end`` as [x,y,z] lists of doubles, the number of seeds (``num_seeds``) to place on the line as well as defining the spacing between seeds (``sampling_type``) as either ``uniform`` or ``random``.
- ``box`` requires the sampling space (``sampling_space``) to be defined (``boundary`` or ``interior``), the sampling type (``sampling_type``) to be defined (``random`` or ``uniform``). By default the boundary of the entire dataset is used, but user can define a new boundary (``x_extents``, ``y_extents``, and ``z_extents``).


At this time, Ascent can only save the output of the particle advection filter as an extract. For rendering, consider using the streamline filter.

Streamlines
~~~~~~~~~~~~
The streamline filter behaves similarly to the particle advection filter, but as the particles are advected, the path of the particle is is collected as a streamline that can be rendered or saved as an extract.
The streamlines are rendered using tubes, which transform the streamline data into a 3D surface.
Tubes are on by default but they can be disabled, though this would also disable rendering capabilities.

.. code-block:: c++

conduit::Node pipelines;
// pipeline 1
pipelines["pl1/f1/type"] = "streamline";
// filter knobs (all these are optional)
conduit::Node &params = pipelines["pl1/f1/params"];
params["field"] = "vel"; // name of the vector field
params["num_steps"] = 1; // number of advection steps
params["step_size"] = 0.01; // advection step size
params["seeds/type"] = "point";
params["seeds/location"] = [-0.826997,-5.62082,3.52779];
//all tubing params are optional
params["enable_tubes"] = "true"; //default: true
params["tube_size"] = 0.4; //default: based on bounds
params["tube_sides"] = 4; //default: 3
params["tube_val"] = 1.0; //default: 0.0
params["tube_capping"] = "true"; //default: true
params["output_field"] = "lines"; //name of streamline tubes for rendering
//default: "field" + "_streamlines"
//e.g "vel_streamlines"

.. figure:: ../images/tout_render_streamlines_point100.png
:scale: 50 %
:align: center

An example of creating a pseudocolor plot of streamline seed placements using ``point``.

.. code-block:: c++

conduit::Node pipelines;
// pipeline 1
pipelines["pl1/f1/type"] = "streamline";
// filter knobs (all these are optional)
conduit::Node &params = pipelines["pl1/f1/params"];
params["field"] = "vel"; // name of the vector field
params["num_steps"] = 1; // number of advection steps
params["step_size"] = 0.01; // advection step size
params["seeds/type"] = "point_list";
params["seeds/location"] = [-9,-9,-9,1,1,1]; // two points
//all tubing params are optional
params["enable_tubes"] = "true"; //default: true
params["tube_size"] = 0.4; //default: based on bounds
params["tube_sides"] = 4; //default: 3
params["tube_val"] = 1.0; //default: 0.0
params["tube_capping"] = "true"; //default: true
params["output_field"] = "lines"; //name of streamline tubes for rendering
//default: "field" + "_streamlines"
//e.g "vel_streamlines"

.. figure:: ../images/tout_render_streamlines_point_list100.png
:scale: 50 %
:align: center

An example of creating a pseudocolor plot of streamline seed placements using ``point_list``.
.. code-block:: c++

conduit::Node pipelines;
// pipeline 1
pipelines["pl1/f1/type"] = "streamline";
// filter knobs (all these are optional)
conduit::Node &params = pipelines["pl1/f1/params"];
params["field"] = "vel"; // name of the vector field
params["num_steps"] = 1; // number of advection steps
params["step_size"] = 0.01; // advection step size
params["seeds/type"] = "line";
//required: how to space the seeds on the line
params["seeds/sampling_type"] = "uniform"; //or "random"
params["seeds/start"] = [-9,-9,-9]; // required: start of line
params["seeds/end"] = [9,9,9]; // required: end of line
params["seeds/num_seeds"] = 10; // required: number of seeds
//all tubing params are optional
params["enable_tubes"] = "true"; //default: true
params["tube_size"] = 0.1; //default: based on bounds
params["tube_sides"] = 4; //default: 3
params["tube_val"] = 1.0; //default: 0.0
params["tube_capping"] = "true"; //default: true
params["output_field"] = "lines"; //name of streamline tubes for rendering
//default: "field" + "_streamlines"
//e.g "vel_streamlines"

.. figure:: ../images/tout_render_streamlines_line100.png
:scale: 50 %
:align: center

An example of creating a pseudocolor plot of streamline seed placements using ``line``.

.. code-block:: c++

conduit::Node pipelines;
// pipeline 1
pipelines["pl1/f1/type"] = "streamline";
// filter knobs (all these are optional)
conduit::Node &params = pipelines["pl1/f1/params"];
params["field"] = "vel"; // name of the vector field
params["step_size"] = 0.01; // advection step size
params["num_steps"] = 1; // number of advection steps
//seed parameters
params["seeds/type"] = "box";
params["seeds/sampling_type"] = "uniform"; //or "random"
params["seeds/sampling_space"] = "interior"; //or "boundary"
//default is using the boundary of the entire dataset
params["seeds/x_extents"] = [-9,9]; //optional: define the boundary
params["seeds/y_extents"] = [-9,9]; //for the distribution
params["seeds/z_extents"] = [-9,9]; //of the particles
//all tubing params are optional
params["enable_tubes"] = "true"; //default: true
params["tube_size"] = 0.1; //default: based on bounds
params["tube_sides"] = 4; //default: 3
params["tube_val"] = 1.0; //default: 0.0
params["tube_capping"] = "true"; //default: true
params["output_field"] = "lines"; //name of streamline tubes for rendering
//default: "field" + "_streamlines"
//e.g "vel_streamlines"

.. figure:: ../images/tout_render_streamlines_box100.png
:scale: 50 %
:align: center

An example of creating a pseudocolor plot of streamline seed placements using ``box``.

Streamlines with Charged Particles (WarpX)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The streamlines with charged particles filter behaves similarly to the streamline filter, but instead utilizes charged particles, which are particles with physical attributes (``charge``, ``mass``, ``momentum``, ``weighting``), that are advected using magnetic (``b_field``) and electric (``e_field``) vector fields.
The resulting streamlines are rendered using tubes, which transform the streamline data into a 3D surface.
Note: the tube functionality is not behaving correctly, currently this functionality is OFF by default.
Otherwise, the resulting streamlines can be saved via an extract.

.. code-block:: c++

conduit::Node pipelines;
// pipeline 1
pipelines["pl1/f1/type"] = "warpx_streamline";
// filter knobs (all these are optional)
conduit::Node &params = pipelines["pl1/f1/params"];
//vector fields
params["b_field"] = "magnetic_field"; //default: B
params["e_field"] = "electric_field"; //default: E
//charged particle params
params["charge_field"] = "charge_field"; //default: Charge
params["mass_field"] = "mass_field"; //default: Mass
params["momentum_field"] = "momentum_field"; //default: Momentum
params["weighting_field"] = "weighting_field"; //default: Weighting
//tubing params
params["enable_tubes"] = "true"; //default: false
params["tube_size"] = 0.2; //default: based on bounds
params["tube_sides"] = 4; //default: 3
params["tube_val"] = 1.0; //default: 0.0
params["tube_capping"] = "true"; //default: true
params["output_field"] = "lines"; //name of streamline tubes for rendering
//default: "b_field" + "e_field" + "_streamlines"
//e.g "B_E_streamlines"

Vector Magnitude
~~~~~~~~~~~~~~~~
Vector magnitude creates a new field on the data set representing the magitude
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit e3e4732

Please sign in to comment.