Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vpp visualization #11274

Merged
merged 3 commits into from May 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,23 @@
# VectorPostprocessorVisualizationAux

## Short Description

!syntax description /AuxKernels/VectorPostprocessorVisualizationAux

## Description

This object is intended to let you view VectorPostprocessor vectors that are of lenght `num_procs` (meaning there is one value per MPI process). This object will take those values and fill up an Auxiliary field with them so the values can be visualized.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess as a utility this is fine, but there are better ways to accomplish this. If you take a step back and look at these new utilities you've added in the past week, you are essentially taking local only information, replicating it on all processors, then using the local only information. We could have just never sent it in the first place if you are only using the AuxKernel, but I suppose you may want both the VPP information and the visualization field.

Copy link
Contributor Author

@friedmud friedmud Apr 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok - can you enumerate the "better ways"? And I'd like to see some citations on these new utilities you've added in the past week, you are essentially taking local only information, replicating it on all processors, then using the local only information

Where?

First of all - I didn't replicate information to all at all with any of my previous utilities. WorkBalance, HistogramVectorPostprocessor and StatisticsVectorPostprocessor only compute final values on processor zero (by default).

Besides that - HistorgramVectorPostprocessor and StatisticsVectorPostprocessor work on any VPP... not just VPPs that compute one value per processor. For instance: you can use it to compute the mean, max, sum, average or get the Histogram of values found by a LineValueSampler... including the statistics/historgram for all of the variables you sampled by that LineValueSampler. Please let me know how else you would do this.

I will agree that in this case for visualization- for WorkBalance - it needs to broadcast to get a copy of the information on each processor - but there might be other reasons for doing that besides just visualizing it. AND this utility is not only intended for WorkBalance alone... I'm going to use it to do many other things with... where I already have the vectors on each processor (like my ray tracing data).

What I'm making here are basic tools. I have many other plans for them going forward with my dissertation. If you don't find them useful then I'll just put them in my app. I know that many of these will get used a TON by many people over the years.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also: please tell me how to get the partition surface area on each processor and paint it into a field only using an AuxKernel?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 resolved


## Important Notes

Note: the VectorPostprocessor must be syncing the vectors it's computing to all processors. By default many just compute to processor 0 (because that's where output occurrs).

For instance: this is the case for [WorkBalance](WorkBalance.md). By default it only syncs to processor 0, but it has a parameter (`sync_to_all_procs`) to tell it to create copies of the vectors on all processors.

!syntax parameters /AuxKernels/VectorPostprocessorVisualizationAux

!syntax inputs /AuxKernels/VectorPostprocessorVisualizationAux

!syntax children /AuxKernels/VectorPostprocessorVisualizationAux

!bibtex bibliography
Expand Up @@ -12,7 +12,9 @@ Currently computes: number of local elements, nodes, dofs and partition sides.

## Important Notes

Note that this VPP only computes the complete vector on processor 0. The vectors this VPP computes may be very large and there is no need to have a copy of them on every processor.
Note that, by default, this VPP only computes the complete vector on processor 0. The vectors this VPP computes may be very large and there is no need to have a copy of them on every processor.

However, you can modify this behavior by setting `sync_to_all_procs = true`

!syntax parameters /VectorPostprocessors/WorkBalance

Expand Down
@@ -0,0 +1,49 @@
//* This file is part of the MOOSE framework
//* https://www.mooseframework.org
//*
//* All rights reserved, see COPYRIGHT for full restrictions
//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
//*
//* Licensed under LGPL 2.1, please see LICENSE for details
//* https://www.gnu.org/licenses/lgpl-2.1.html

#ifndef VECTORPOSTPROCESSORVISUALIZATIONAUX_H
#define VECTORPOSTPROCESSORVISUALIZATIONAUX_H

#include "AuxKernel.h"

// Forward Declarations
class VectorPostprocessorVisualizationAux;

template <>
InputParameters validParams<VectorPostprocessorVisualizationAux>();

/**
* Read values from a VectorPostprocessor that is producing vectors that are "number of processors"
* in length. Puts the value for each processor into an elemental auxiliary field.
*/
class VectorPostprocessorVisualizationAux : public AuxKernel
{
public:
VectorPostprocessorVisualizationAux(const InputParameters & parameters);

protected:
/**
* Note: this used for error checking. It's done very late because VPP's don't fill in their
* vectors until they are computed
*/
virtual void timestepSetup() override;

/**
* Get the value from the vector and assign it to the element
*/
virtual Real computeValue() override;

/// Holds the values we want to display
const VectorPostprocessorValue & _vpp_vector;

/// Optimization
processor_id_type _my_pid;
};

#endif // VECTORPOSTPROCESSORVISUALIZATIONAUX_H
4 changes: 3 additions & 1 deletion framework/include/vectorpostprocessors/WorkBalance.h
Expand Up @@ -40,9 +40,11 @@ class WorkBalance : public GeneralVectorPostprocessor
virtual void finalize() override;

protected:
// The system to count DoFs from
/// The system to count DoFs from
int _system;

bool _sync_to_all_procs;

dof_id_type _local_num_elems;
dof_id_type _local_num_nodes;
dof_id_type _local_num_dofs;
Expand Down
60 changes: 60 additions & 0 deletions framework/src/auxkernels/VectorPostprocessorVisualizationAux.C
@@ -0,0 +1,60 @@
//* This file is part of the MOOSE framework
//* https://www.mooseframework.org
//*
//* All rights reserved, see COPYRIGHT for full restrictions
//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
//*
//* Licensed under LGPL 2.1, please see LICENSE for details
//* https://www.gnu.org/licenses/lgpl-2.1.html

#include "VectorPostprocessorVisualizationAux.h"

registerMooseObject("MooseApp", VectorPostprocessorVisualizationAux);

template <>
InputParameters
validParams<VectorPostprocessorVisualizationAux>()
{
InputParameters params = validParams<AuxKernel>();

params.addClassDescription("Read values from a VectorPostprocessor that is producing vectors "
"that are 'number of processors' * in length. Puts the value for "
"each processor into an elemental auxiliary field.");

params.addRequiredParam<VectorPostprocessorName>(
"vpp", "The name of the VectorPostprocessor to pull the data from.");
params.addRequiredParam<std::string>(
"vector_name", "The name of the vector to use from the VectorPostprocessor");

return params;
}

VectorPostprocessorVisualizationAux::VectorPostprocessorVisualizationAux(
const InputParameters & parameters)
: AuxKernel(parameters),
_vpp_vector(getVectorPostprocessorValue("vpp", getParam<std::string>("vector_name"))),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting that this works here. It actually isn't correct. We have all of those "ByName" variants, which take the parameter name (instead of the actual name). This might mean we have another bug in the retrieval of VPPs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's right the way it is - it is using the non "by name" version on purpose. vpp is the parameter name.

There is no such thing for the vector_name though (which is why I have to do getParam() for it). It could be argued that we should make that part of the interface too...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

_my_pid(processor_id())
{
}

void
VectorPostprocessorVisualizationAux::timestepSetup()
{
if (_vpp_vector.size() != n_processors())
mooseError("Error in VectorPostprocessor ",
name(),
". Vector ",
getParam<std::string>("vector_name"),
" in VectorPostprocessor ",
getParam<VectorPostprocessorName>("vpp"),
" does not contain num_procs number of entries. num_procs: ",
n_processors(),
" num_entries: ",
_vpp_vector.size());
}

Real
VectorPostprocessorVisualizationAux::computeValue()
{
return _vpp_vector[_my_pid];
}
3 changes: 2 additions & 1 deletion framework/src/problems/FEProblemBase.C
Expand Up @@ -2720,7 +2720,8 @@ VectorPostprocessorValue &
FEProblemBase::getVectorPostprocessorValue(const VectorPostprocessorName & name,
const std::string & vector_name)
{
return _vpps_data.getVectorPostprocessorValue(name, vector_name);
auto & val = _vpps_data.getVectorPostprocessorValue(name, vector_name);
return val;
}

VectorPostprocessorValue &
Expand Down
32 changes: 26 additions & 6 deletions framework/src/vectorpostprocessors/WorkBalance.C
Expand Up @@ -34,12 +34,20 @@ validParams<WorkBalance>()
system_enum,
"The system(s) to retrieve the number of DOFs from (NL, AUX, ALL). Default == ALL");

params.addParam<bool>("sync_to_all_procs",
false,
"Whether or not to sync the vectors to all processors. By default we only "
"sync them to processor 0 so they can be written out. Setting this to "
"true will use more communication, but is necessary if you expect these "
"vectors to be available on all processors");

return params;
}

WorkBalance::WorkBalance(const InputParameters & parameters)
: GeneralVectorPostprocessor(parameters),
_system(getParam<MooseEnum>("system")),
_sync_to_all_procs(getParam<bool>("sync_to_all_procs")),
_local_num_elems(0),
_local_num_nodes(0),
_local_num_dofs(0),
Expand Down Expand Up @@ -245,12 +253,24 @@ WorkBalance::execute()
void
WorkBalance::finalize()
{
// Gather the results down to processor 0
_communicator.gather(0, static_cast<Real>(_local_num_elems), _num_elems);
_communicator.gather(0, static_cast<Real>(_local_num_nodes), _num_nodes);
_communicator.gather(0, static_cast<Real>(_local_num_dofs), _num_dofs);
_communicator.gather(0, static_cast<Real>(_local_num_partition_sides), _num_partition_sides);
_communicator.gather(0, _local_partition_surface_area, _partition_surface_area);
if (!_sync_to_all_procs)
{
// Gather the results down to processor 0
_communicator.gather(0, static_cast<Real>(_local_num_elems), _num_elems);
_communicator.gather(0, static_cast<Real>(_local_num_nodes), _num_nodes);
_communicator.gather(0, static_cast<Real>(_local_num_dofs), _num_dofs);
_communicator.gather(0, static_cast<Real>(_local_num_partition_sides), _num_partition_sides);
_communicator.gather(0, _local_partition_surface_area, _partition_surface_area);
}
else
{
// Gather the results down to all procs
_communicator.allgather(static_cast<Real>(_local_num_elems), _num_elems);
_communicator.allgather(static_cast<Real>(_local_num_nodes), _num_nodes);
_communicator.allgather(static_cast<Real>(_local_num_dofs), _num_dofs);
_communicator.allgather(static_cast<Real>(_local_num_partition_sides), _num_partition_sides);
_communicator.allgather(_local_partition_surface_area, _partition_surface_area);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This really isn't the right idea here. You should be usingscatter instead of allgather:
https://github.com/libMesh/libmesh/blob/master/include/parallel/parallel.h#L1240-L1247

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. allgather() is the right one.

All processors are contributing one value that gets inserted in their rank position on every processor. That's exactly what allgather() does.

Read up: http://mpitutorial.com/tutorials/mpi-scatter-gather-and-allgather/

Copy link
Member

@permcody permcody Apr 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to record our conversation that we had offline here: What I was after here was gather followed by scatter, not scatter instead of allgather. However, the scatter step should really be part of the AuxKernel, not the VPP. We decided that this isn't necessary for now as it won't impede scalability until we reach at least 100K - 1M procs.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW - I thought of something else that could help out here. We have the UserObject::spatialValue() and associated AuxKernel that pulls those out. If we had a similar interface for VPPs, you could actually scatter within your VPP and just store the scalar that you want to dump out. This would put the logic back into the VPP instead of the AuxKernel.

I think we should consider fleshing this interface out more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 resolved

}

// Fill in the PID column - this just makes plotting easier
_pid.resize(_num_elems.size());
Expand Down
Binary file not shown.
10 changes: 10 additions & 0 deletions test/tests/auxkernels/vector_postprocessor_visualization/tests
@@ -0,0 +1,10 @@
[Tests]
[./test]
type = 'Exodiff'
input = 'vector_postprocessor_visualization.i'
exodiff = 'vector_postprocessor_visualization_out.e'
min_parallel = 3
max_parallel = 3
mesh_mode = replicated # Just because of the gold file
[../]
[]
@@ -0,0 +1,82 @@
[Mesh]
type = GeneratedMesh
dim = 2
nx = 10
ny = 10
parallel_type = REPLICATED
partitioner = linear
[]

[Variables]
[u]
[]
[]

[Kernels]
[diff]
type = Diffusion
variable = u
[]
[]

[BCs]
[left]
type = DirichletBC
variable = u
boundary = 'left'
value = 0
[]
[right]
type = DirichletBC
variable = u
boundary = 'right'
value = 1
[]
[]

[Executioner]
type = Steady
solve_type = PJFNK
petsc_options_iname = '-pc_type -pc_hypre_type'
petsc_options_value = 'hypre boomeramg'
[]

[Outputs]
exodus = true
[]

[AuxVariables]
[num_elems]
family = MONOMIAL
order = CONSTANT
[]
[partition_surface_area]
family = MONOMIAL
order = CONSTANT
[]
[]

[AuxKernels]
[wb_num_elems]
type = VectorPostprocessorVisualizationAux
vpp = 'wb'
vector_name = num_elems
variable = num_elems
execute_on = 'TIMESTEP_END'
[]
[wb_partition_surface_area]
type = VectorPostprocessorVisualizationAux
vpp = 'wb'
vector_name = partition_surface_area
variable = partition_surface_area
execute_on = 'TIMESTEP_END'
[]
[]

[VectorPostprocessors]
[wb]
type = WorkBalance
sync_to_all_procs = 'true'
execute_on = 'INITIAL'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be 'TIMESTEP_END' too if you are visualizing every timestep (see above)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope - the issue is a dependency resolution issue.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh! Then this is a bug! VectorPostprocessors should be using the same PRE/POST logic as PostProcessors (and UserObjects). I'll checkout your branch and see if I can fix this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK - So there isn't a bug here. Turns out that the check in VectorPostprocessorVisualizationAux::timestepSetup() is overzealous in that it runs in only one spot (e.g. at the beginning of the timestep). The only way to get around that is to run the VPP that you are trying to visualize before that, which only leaves INITIAL. However, that defeats the purpose of the check since VPPs are allowed to change the size of their vectors with each invocation.

[]
[]