Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Complex-valued and single-precision data output in ExodusII/Nemesis format #195

Merged
merged 1 commit into from

5 participants

@danac
Collaborator

This commit implements correct complex-valued output to ExodusII and Nemesis formats. In addition, a flag is added to the ExodusII_IO constructor to force single-precision output.

Both features have been tested with nodal data in ExodusII format with the reduced_basis_ex7 example (Nemesis and elemental data not tested yet).

@dknez
Collaborator

Thanks Dana!

All: This resolves #47 that I opened a while ago, so it'd be good to merge it!

@roystgnr
Owner

My vote would be to merge right after the next release.

@dknez
Collaborator

Sounds good to me.

@friedmud
Owner

I can verify that MOOSE passes with these changes.

What's odd is that GitHub says it can't merge this but I didn't have any problems merging it on the command-line....

I have to admit though - it seems like a lot of extra complexity just to output single precision. Why do you want to output single precision again?

@dknez
Collaborator

Regarding the complexity of the patch: I'd say that most of the patch is to fix the --enable-complex case, which has never been supported by our ExodusII IO. This patch follows the same approach that is used in GMVIO for --enable-complex.

Though yes, there are also a bunch of changes related to support for single precision. Single precision vs double precision is indistinguishable in the "eyeball norm", so it's nice to have the option to do single precision in order to reduce file size. We have a client/server setup and send a lot of visualization data over the network, so reducing file size in this way is certainly helpful.

@dknez
Collaborator

Now that 0.9.3 is released, I'd like to go ahead with this merge, as per Roy's comment above. Any objections?

@danac
Collaborator

I squashed/rebased with master and fixed one conflict with commit 91c7a30 in exoduxII_io.C

@benkirk
Owner

Thanks for rebasing - no issues.

@benkirk benkirk merged commit 7a0abae into libMesh:master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 17, 2014
  1. @danac
This page is out of date. Refresh to see the latest.
View
7 examples/reduced_basis/reduced_basis_ex7/reduced_basis_ex7.C
@@ -170,7 +170,14 @@ int main (int argc, char** argv)
// Plot the solution
rb_con.load_rb_solution();
+ //Output the variable in GMV format
GMVIO(mesh).write_equation_systems ("RB_sol.gmv",equation_systems);
+
+ //Output the variable in ExodusII format
+ ExodusII_IO(mesh).write_equation_systems ("RB_sol_double.exo", equation_systems);
+
+ //Output the variable in ExodusII format (single precision)
+ ExodusII_IO(mesh, /*single_precision=*/true).write_equation_systems ("RB_sol_float.exo", equation_systems);
}
// Now do a sweep over frequencies and write out the reflection coefficient for each frequency
View
2  include/mesh/exodusII_io.h
@@ -60,7 +60,7 @@ class ExodusII_IO : public MeshInput<MeshBase>,
* This is the constructor required to read a mesh.
*/
explicit
- ExodusII_IO (MeshBase& mesh);
+ ExodusII_IO (MeshBase& mesh, bool single_precision=false);
/**
* Destructor.
View
22 include/mesh/exodusII_io_helper.h
@@ -71,7 +71,8 @@ class ExodusII_IO_Helper : public ParallelObject
*/
ExodusII_IO_Helper(const ParallelObject &parent,
bool v=false,
- bool run_only_on_proc0=true);
+ bool run_only_on_proc0=true,
+ bool single_precision=false);
/**
* Destructor.
*/
@@ -297,13 +298,13 @@ class ExodusII_IO_Helper : public ParallelObject
/**
* Writes the vector of values to the element variables.
*/
- void write_element_values(const MeshBase & mesh, const std::vector<Number> & values, int timestep);
-
+ void write_element_values(const MeshBase & mesh, const std::vector<Real> & values, int timestep);
+
/**
* Writes the vector of values to a nodal variable.
*/
- void write_nodal_values(int var_id, const std::vector<Number> & values, int timestep);
-
+ void write_nodal_values(int var_id, const std::vector<Real> & values, int timestep);
+
/**
* Writes the vector of information records.
*/
@@ -312,7 +313,7 @@ class ExodusII_IO_Helper : public ParallelObject
/**
* Writes the vector of global variables.
*/
- void write_global_values(const std::vector<Number> & values, int timestep);
+ void write_global_values(const std::vector<Real> & values, int timestep);
/**
* Sets the underlying value of the boolean flag
@@ -331,6 +332,12 @@ class ExodusII_IO_Helper : public ParallelObject
void set_coordinate_offset(Point p);
/**
+ * Returns a vector with three copies of each element in the provided name vector,
+ * starting with r_, i_ and a_ respectively.
+ */
+ std::vector<std::string> get_complex_names(const std::vector<std::string>& names) const;
+
+ /**
* This is the \p ExodusII_IO_Helper Conversion class. It provides
* a data structure which contains \p ExodusII node/edge maps and
* name conversions. It's defined below.
@@ -544,6 +551,9 @@ class ExodusII_IO_Helper : public ParallelObject
// On output, shift every point by _coordinate_offset
Point _coordinate_offset;
+
+ // If true, forces single precision I/O
+ bool _single_precision;
private:
/**
View
2  include/mesh/nemesis_io.h
@@ -62,7 +62,7 @@ class Nemesis_IO_Helper;
* This is the constructor required to read a mesh.
*/
explicit
- Nemesis_IO (MeshBase& mesh);
+ Nemesis_IO (MeshBase& mesh, bool single_precision=false);
/**
* Destructor.
View
2  include/mesh/nemesis_io_helper.h
@@ -65,7 +65,7 @@ class Nemesis_IO_Helper : public ExodusII_IO_Helper
*/
explicit
Nemesis_IO_Helper(const ParallelObject &parent,
- bool verbose=false);
+ bool verbose=false, bool single_precision=false);
/**
* Destructor.
View
160 src/mesh/exodusII_io.C
@@ -19,6 +19,8 @@
// C++ includes
#include <fstream>
#include <cstring>
+#include <sstream>
+#include <map>
// Local includes
#include "libmesh/exodusII_io.h"
@@ -37,12 +39,12 @@ namespace libMesh
// ------------------------------------------------------------
// ExodusII_IO class members
-ExodusII_IO::ExodusII_IO (MeshBase& mesh) :
+ExodusII_IO::ExodusII_IO (MeshBase& mesh, bool single_precision) :
MeshInput<MeshBase> (mesh),
MeshOutput<MeshBase> (mesh),
ParallelObject(mesh),
#ifdef LIBMESH_HAVE_EXODUS_API
- exio_helper(new ExodusII_IO_Helper(*this)),
+ exio_helper(new ExodusII_IO_Helper(*this, false, true, single_precision)),
#endif
_timestep(1),
_verbose(false),
@@ -535,9 +537,47 @@ void ExodusII_IO::write_element_data (const EquationSystems & es)
return;
const MeshBase & mesh = MeshOutput<MeshBase>::mesh();
-
+
+#ifdef LIBMESH_USE_COMPLEX_NUMBERS
+
+ std::vector<std::string> complex_names = exio_helper->get_complex_names(names);
+
+ exio_helper->initialize_element_variables(complex_names);
+
+ unsigned int num_values = soln.size();
+ unsigned int num_vars = names.size();
+ unsigned int num_elems = num_values / num_vars;
+
+ // This will contain the real and imaginary parts and the magnitude
+ // of the values in soln
+ std::vector<Real> complex_soln(3*num_values);
+
+ for(unsigned i(0); i < num_vars; ++i)
+ {
+
+ for(unsigned int j(0); j < num_elems; ++j)
+ {
+ Number value = soln[i*num_vars + j];
+ complex_soln[3*i*num_elems + j] = value.real();
+ }
+ for(unsigned int j(0); j < num_elems; ++j)
+ {
+ Number value = soln[i*num_vars + j];
+ complex_soln[3*i*num_elems + num_elems +j] = value.imag();
+ }
+ for(unsigned int j(0); j < num_elems; ++j)
+ {
+ Number value = soln[i*num_vars + j];
+ complex_soln[3*i*num_elems + 2*num_elems + j] = std::abs(value);
+ }
+ }
+
+ exio_helper->write_element_values(mesh, complex_soln, _timestep);
+
+#else
exio_helper->initialize_element_variables(names);
exio_helper->write_element_values(mesh, soln, _timestep);
+#endif
}
@@ -561,8 +601,17 @@ void ExodusII_IO::write_nodal_data (const std::string& fname,
else
output_names = names;
+#ifdef LIBMESH_USE_COMPLEX_NUMBERS
+
+ std::vector<std::string> complex_names = exio_helper->get_complex_names(names);
+
+ // Call helper function for opening/initializing data, giving it the
+ // complex variable names
+ this->write_nodal_data_common(fname, complex_names, /*continuous=*/true);
+#else
// Call helper function for opening/initializing data
this->write_nodal_data_common(fname, output_names, /*continuous=*/true);
+#endif
if(mesh.processor_id())
{
@@ -573,21 +622,39 @@ void ExodusII_IO::write_nodal_data (const std::string& fname,
// This will count the number of variables actually output
for (int c=0; c<num_vars; c++)
{
+ std::stringstream name_to_find;
+
std::vector<std::string>::iterator pos =
std::find(output_names.begin(), output_names.end(), names[c]);
if (pos == output_names.end())
continue;
unsigned int variable_name_position =
- libmesh_cast_int<unsigned int>(pos - output_names.begin());
-
+ libmesh_cast_int<unsigned int>(pos - output_names.begin());
+
+#ifdef LIBMESH_USE_COMPLEX_NUMBERS
+ std::vector<Real> real_parts(num_nodes);
+ std::vector<Real> imag_parts(num_nodes);
+ std::vector<Real> magnitudes(num_nodes);
+
+ for(unsigned int i(0); i < num_nodes; ++i)
+ {
+ real_parts[i] = soln[i*num_vars + c].real();
+ imag_parts[i] = soln[i*num_vars + c].imag();
+ magnitudes[i] = std::abs(soln[i*num_vars + c]);
+ }
+ exio_helper->write_nodal_values(3*variable_name_position+1,real_parts,_timestep);
+ exio_helper->write_nodal_values(3*variable_name_position+2,imag_parts,_timestep);
+ exio_helper->write_nodal_values(3*variable_name_position+3,magnitudes,_timestep);
+#else
std::vector<Number> cur_soln(num_nodes);
// Copy out this variable's solution
for(dof_id_type i=0; i<num_nodes; i++)
cur_soln[i] = soln[i*num_vars + c];
-
exio_helper->write_nodal_values(variable_name_position+1,cur_soln,_timestep);
+#endif
+
}
STOP_LOG("write_nodal_data()", "ExodusII_IO");
@@ -627,9 +694,47 @@ void ExodusII_IO::write_global_data (const std::vector<Number>& soln,
<< std::endl;
libmesh_error();
}
-
+
+#ifdef LIBMESH_USE_COMPLEX_NUMBERS
+
+ std::vector<std::string> complex_names = exio_helper->get_complex_names(names);
+
+ exio_helper->initialize_global_variables(complex_names);
+
+ unsigned int num_values = soln.size();
+ unsigned int num_vars = names.size();
+ unsigned int num_elems = num_values / num_vars;
+
+ // This will contain the real and imaginary parts and the magnitude
+ // of the values in soln
+ std::vector<Real> complex_soln(3*num_values);
+
+ for(unsigned i(0); i < num_vars; ++i)
+ {
+
+ for(unsigned int j(0); j < num_elems; ++j)
+ {
+ Number value = soln[i*num_vars + j];
+ complex_soln[3*i*num_elems + j] = value.real();
+ }
+ for(unsigned int j(0); j < num_elems; ++j)
+ {
+ Number value = soln[i*num_vars + j];
+ complex_soln[3*i*num_elems + num_elems +j] = value.imag();
+ }
+ for(unsigned int j(0); j < num_elems; ++j)
+ {
+ Number value = soln[i*num_vars + j];
+ complex_soln[3*i*num_elems + 2*num_elems + j] = std::abs(value);
+ }
+ }
+
+ exio_helper->write_global_values(complex_soln, _timestep);
+
+#else
exio_helper->initialize_global_variables(names);
exio_helper->write_global_values(soln, _timestep);
+#endif
}
@@ -694,6 +799,7 @@ void ExodusII_IO::write_nodal_data_discontinuous (const std::string& fname,
const std::vector<Number>& soln,
const std::vector<std::string>& names)
{
+
START_LOG("write_nodal_data_discontinuous()", "ExodusII_IO");
const MeshBase & mesh = MeshOutput<MeshBase>::mesh();
@@ -705,8 +811,17 @@ void ExodusII_IO::write_nodal_data_discontinuous (const std::string& fname,
for ( ; it != end; ++it)
num_nodes += (*it)->n_nodes();
+#ifdef LIBMESH_USE_COMPLEX_NUMBERS
+
+ std::vector<std::string> complex_names = exio_helper->get_complex_names(names);
+
+ // Call helper function for opening/initializing data, giving it the
+ // complex variable names
+ this->write_nodal_data_common(fname, complex_names, /*continuous=*/false);
+#else
// Call helper function for opening/initializing data
this->write_nodal_data_common(fname, names, /*continuous=*/false);
+#endif
if (mesh.processor_id())
{
@@ -715,16 +830,31 @@ void ExodusII_IO::write_nodal_data_discontinuous (const std::string& fname,
}
for (int c=0; c<num_vars; c++)
- {
- // Copy out this variable's solution
- std::vector<Number> cur_soln(num_nodes);
-
- for(int i=0; i<num_nodes; i++)
- cur_soln[i] = soln[i*num_vars + c];
+ {
+#ifdef LIBMESH_USE_COMPLEX_NUMBERS
+ std::vector<Real> real_parts(num_nodes);
+ std::vector<Real> imag_parts(num_nodes);
+ std::vector<Real> magnitudes(num_nodes);
+
+ for(int i(0); i < num_nodes; ++i)
+ {
+ real_parts[i] = soln[i*num_vars + c].real();
+ imag_parts[i] = soln[i*num_vars + c].imag();
+ magnitudes[i] = std::abs(soln[i*num_vars + c]);
+ }
+ exio_helper->write_nodal_values(3*c+1,real_parts,_timestep);
+ exio_helper->write_nodal_values(3*c+2,imag_parts,_timestep);
+ exio_helper->write_nodal_values(3*c+3,magnitudes,_timestep);
+#else
+ // Copy out this variable's solution
+ std::vector<Number> cur_soln(num_nodes);
- exio_helper->write_nodal_values(c+1,cur_soln,_timestep);
- }
+ for(int i=0; i<num_nodes; i++)
+ cur_soln[i] = soln[i*num_vars + c];
+ exio_helper->write_nodal_values(c+1,cur_soln,_timestep);
+ }
+#endif
STOP_LOG("write_nodal_data_discontinuous()", "ExodusII_IO");
}
View
198 src/mesh/exodusII_io_helper.C
@@ -224,7 +224,8 @@ const int ExodusII_IO_Helper::ElementMaps::pyramid_inverse_face_map[5] = {-1,-1,
ExodusII_IO_Helper::ExodusII_IO_Helper(const ParallelObject &parent,
bool v,
- bool run_only_on_proc0) :
+ bool run_only_on_proc0,
+ bool single_precision) :
ParallelObject(parent),
ex_id(0),
ex_err(0),
@@ -249,7 +250,8 @@ const int ExodusII_IO_Helper::ElementMaps::pyramid_inverse_face_map[5] = {-1,-1,
_elem_vars_initialized(false),
_global_vars_initialized(false),
_nodal_vars_initialized(false),
- _use_mesh_dimension_instead_of_spatial_dimension(false)
+ _use_mesh_dimension_instead_of_spatial_dimension(false),
+ _single_precision(single_precision)
{
title.resize(MAX_LINE_LENGTH+1);
elem_type.resize(MAX_STR_LENGTH);
@@ -967,10 +969,20 @@ void ExodusII_IO_Helper::create(std::string filename)
// call create there.
if ((this->processor_id() == 0) || (!_run_only_on_proc0))
{
+ int comp_ws(0), io_ws(0);
+
+ if(_single_precision)
+ {
+ comp_ws = sizeof(float);
+ io_ws = sizeof(float);
+ }
// Fall back on double precision when necessary since ExodusII
// doesn't seem to support long double
- int comp_ws = std::min(sizeof(Real), sizeof(double));
- int io_ws = std::min(sizeof(Real), sizeof(double));
+ else
+ {
+ comp_ws = std::min(sizeof(Real), sizeof(double));
+ io_ws = std::min(sizeof(Real), sizeof(double));
+ }
ex_id = exII::ex_create(filename.c_str(), EX_CLOBBER, &comp_ws, &io_ws);
@@ -1132,31 +1144,50 @@ void ExodusII_IO_Helper::write_nodal_coordinates(const MeshBase & mesh)
MeshBase::const_node_iterator it = mesh.nodes_begin();
const MeshBase::const_node_iterator end = mesh.nodes_end();
for (unsigned i = 0; it != end; ++it, ++i)
- {
- const Node* node = *it;
+ {
+ const Node* node = *it;
- x[i] = (*node)(0) + _coordinate_offset(0);
+ x[i] = (*node)(0) + _coordinate_offset(0);
#if LIBMESH_DIM > 1
- y[i]=(*node)(1) + _coordinate_offset(1);
+ y[i]=(*node)(1) + _coordinate_offset(1);
#else
- y[i]=0.;
+ y[i]=0.;
#endif
#if LIBMESH_DIM > 2
- z[i]=(*node)(2) + _coordinate_offset(2);
+ z[i]=(*node)(2) + _coordinate_offset(2);
#else
- z[i]=0.;
+ z[i]=0.;
#endif
- // Fill in node_num_map entry with the proper (1-based) node id
- node_num_map[i] = node->id() + 1;
- }
+ // Fill in node_num_map entry with the proper (1-based) node id
+ node_num_map[i] = node->id() + 1;
+ }
}
-
- ex_err = exII::ex_put_coord(ex_id,
+
+ if(_single_precision)
+ {
+ std::vector<float> x_single(num_nodes), y_single(num_nodes), z_single(num_nodes);
+ for(int i(0); i < num_nodes; ++i)
+ {
+ x_single[i] = static_cast<float>(x[i]);
+ y_single[i] = static_cast<float>(y[i]);
+ z_single[i] = static_cast<float>(z[i]);
+ }
+
+ ex_err = exII::ex_put_coord(ex_id,
+ x_single.empty() ? NULL : &x_single[0],
+ y_single.empty() ? NULL : &y_single[0],
+ z_single.empty() ? NULL : &z_single[0]);
+ }
+ else
+ {
+ ex_err = exII::ex_put_coord(ex_id,
x.empty() ? NULL : &x[0],
y.empty() ? NULL : &y[0],
z.empty() ? NULL : &z[0]);
+ }
+
EX_CHECK_ERR(ex_err, "Error writing coordinates to Exodus file.");
@@ -1197,10 +1228,28 @@ void ExodusII_IO_Helper::write_nodal_coordinates_discontinuous(const MeshBase &
i++;
}
- ex_err = exII::ex_put_coord(ex_id,
+ if(_single_precision)
+ {
+ std::vector<float> x_single(num_nodes), y_single(num_nodes), z_single(num_nodes);
+ for(int i(0); i < num_nodes; ++i)
+ {
+ x_single[i] = static_cast<float>(x[i]);
+ y_single[i] = static_cast<float>(y[i]);
+ z_single[i] = static_cast<float>(z[i]);
+ }
+
+ ex_err = exII::ex_put_coord(ex_id,
+ x_single.empty() ? NULL : &x_single[0],
+ y_single.empty() ? NULL : &y_single[0],
+ z_single.empty() ? NULL : &z_single[0]);
+ }
+ else
+ {
+ ex_err = exII::ex_put_coord(ex_id,
x.empty() ? NULL : &x[0],
y.empty() ? NULL : &y[0],
z.empty() ? NULL : &z[0]);
+ }
EX_CHECK_ERR(ex_err, "Error writing coordinates to Exodus file.");
}
@@ -1224,10 +1273,10 @@ void ExodusII_IO_Helper::write_elements(const MeshBase & mesh)
const MeshBase::const_element_iterator end = mesh.active_elements_end();
//loop through element and map between block and element vector
for (; mesh_it!=end; ++mesh_it)
- {
- const Elem * elem = *mesh_it;
- subdomain_map[ elem->subdomain_id() ].push_back(elem->id());
- }
+ {
+ const Elem * elem = *mesh_it;
+ subdomain_map[ elem->subdomain_id() ].push_back(elem->id());
+ }
// element map vector
num_elem_blk = subdomain_map.size();
@@ -1694,7 +1743,7 @@ void ExodusII_IO_Helper::write_timestep(int timestep, Real time)
-void ExodusII_IO_Helper::write_element_values(const MeshBase & mesh, const std::vector<Number> & values, int timestep)
+void ExodusII_IO_Helper::write_element_values(const MeshBase & mesh, const std::vector<Real> & values, int timestep)
{
if ((_run_only_on_proc0) && (this->processor_id() != 0))
return;
@@ -1726,17 +1775,34 @@ void ExodusII_IO_Helper::write_element_values(const MeshBase & mesh, const std::
{
const std::vector<unsigned int> & elem_nums = (*it).second;
const unsigned int num_elems_this_block = elem_nums.size();
- std::vector<Number> data(num_elems_this_block);
+ std::vector<Real> data(num_elems_this_block);
for (unsigned int k=0; k<num_elems_this_block; ++k)
data[k] = values[i*num_elem + elem_nums[k]];
- ex_err = exII::ex_put_elem_var(ex_id,
+ if(_single_precision)
+ {
+ std::vector<float> cast_data(num_elems_this_block);
+ for(unsigned int l(0); i < num_elems_this_block; ++l)
+ {
+ cast_data[l] = static_cast<float>(data[l]);
+ }
+ ex_err = exII::ex_put_elem_var(ex_id,
+ timestep,
+ i+1,
+ this->get_block_id(j),
+ num_elems_this_block,
+ &cast_data[0]);
+ }
+ else
+ {
+ ex_err = exII::ex_put_elem_var(ex_id,
timestep,
i+1,
this->get_block_id(j),
num_elems_this_block,
&data[0]);
+ }
EX_CHECK_ERR(ex_err, "Error writing element values.");
}
}
@@ -1747,12 +1813,25 @@ void ExodusII_IO_Helper::write_element_values(const MeshBase & mesh, const std::
-void ExodusII_IO_Helper::write_nodal_values(int var_id, const std::vector<Number> & values, int timestep)
+void ExodusII_IO_Helper::write_nodal_values(int var_id, const std::vector<Real> & values, int timestep)
{
if ((_run_only_on_proc0) && (this->processor_id() != 0))
return;
- ex_err = exII::ex_put_nodal_var(ex_id, timestep, var_id, num_nodes, &values[0]);
+ if(_single_precision)
+ {
+ unsigned int num_values = values.size();
+ std::vector<float> cast_values(num_values);
+ for(unsigned int i(0); i < num_values; ++i)
+ {
+ cast_values[i] = static_cast<float>(values[i]);
+ }
+ ex_err = exII::ex_put_nodal_var(ex_id, timestep, var_id, num_nodes, &cast_values[0]);
+ }
+ else
+ {
+ ex_err = exII::ex_put_nodal_var(ex_id, timestep, var_id, num_nodes, &values[0]);
+ }
EX_CHECK_ERR(ex_err, "Error writing nodal values.");
ex_err = exII::ex_update(ex_id);
@@ -1782,30 +1861,43 @@ void ExodusII_IO_Helper::write_information_records(const std::vector<std::string
int num_records = records.size();
if (num_records > 0)
- {
- NamesData info(num_records, MAX_LINE_LENGTH);
+ {
+ NamesData info(num_records, MAX_LINE_LENGTH);
- // If an entry is longer than MAX_LINE_LENGTH characters it's not an error, we just
- // write the first MAX_LINE_LENGTH characters to the file.
- for (unsigned i=0; i<records.size(); ++i)
- info.push_back_entry(records[i]);
+ // If an entry is longer than MAX_LINE_LENGTH characters it's not an error, we just
+ // write the first MAX_LINE_LENGTH characters to the file.
+ for (unsigned i=0; i<records.size(); ++i)
+ info.push_back_entry(records[i]);
- ex_err = exII::ex_put_info(ex_id, num_records, info.get_char_star_star());
- EX_CHECK_ERR(ex_err, "Error writing global values.");
+ ex_err = exII::ex_put_info(ex_id, num_records, info.get_char_star_star());
+ EX_CHECK_ERR(ex_err, "Error writing global values.");
- ex_err = exII::ex_update(ex_id);
- EX_CHECK_ERR(ex_err, "Error flushing buffers to file.");
- }
+ ex_err = exII::ex_update(ex_id);
+ EX_CHECK_ERR(ex_err, "Error flushing buffers to file.");
+ }
}
-void ExodusII_IO_Helper::write_global_values(const std::vector<Number> & values, int timestep)
+void ExodusII_IO_Helper::write_global_values(const std::vector<Real> & values, int timestep)
{
if ((_run_only_on_proc0) && (this->processor_id() != 0))
return;
-
- ex_err = exII::ex_put_glob_vars(ex_id, timestep, num_global_vars, &values[0]);
+
+ if(_single_precision)
+ {
+ unsigned int num_values = values.size();
+ std::vector<float> cast_values(num_values);
+
+ for(unsigned int i(0); i < num_values; ++i)
+ cast_values[i] = static_cast<float>(values[i]);
+
+ ex_err = exII::ex_put_glob_vars(ex_id, timestep, num_global_vars, &cast_values[0]);
+ }
+ else
+ {
+ ex_err = exII::ex_put_glob_vars(ex_id, timestep, num_global_vars, &values[0]);
+ }
EX_CHECK_ERR(ex_err, "Error writing global values.");
ex_err = exII::ex_update(ex_id);
@@ -1825,6 +1917,32 @@ void ExodusII_IO_Helper::set_coordinate_offset(Point p)
}
+std::vector<std::string> ExodusII_IO_Helper::get_complex_names(const std::vector<std::string>& names) const
+{
+ std::vector<std::string>::const_iterator names_it = names.begin();
+ std::vector<std::string>::const_iterator names_end = names.end();
+
+ std::vector<std::string> complex_names;
+
+ // This will loop over all names and create new "complex" names
+ // (i.e. names that start with r_, i_ or a_
+ for(; names_it != names_end; ++names_it)
+ {
+ std::cout << "VARIABLE: " << *names_it << std::endl;
+ std::stringstream name_real, name_imag, name_abs;
+ name_real << "r_" << *names_it;
+ name_imag << "i_" << *names_it;
+ name_abs << "a_" << *names_it;
+
+ complex_names.push_back(name_real.str());
+ complex_names.push_back(name_imag.str());
+ complex_names.push_back(name_abs.str());
+ }
+
+ return complex_names;
+}
+
+
// ------------------------------------------------------------
// ExodusII_IO_Helper::Conversion class members
View
121 src/mesh/nemesis_io.C
@@ -77,12 +77,12 @@ namespace {
// ------------------------------------------------------------
// Nemesis_IO class members
-Nemesis_IO::Nemesis_IO (MeshBase& mesh) :
+Nemesis_IO::Nemesis_IO (MeshBase& mesh, bool single_precision) :
MeshInput<MeshBase> (mesh, /*is_parallel_format=*/true),
MeshOutput<MeshBase> (mesh, /*is_parallel_format=*/true),
ParallelObject (mesh),
#if defined(LIBMESH_HAVE_EXODUS_API) && defined(LIBMESH_HAVE_NEMESIS_API)
- nemhelper(new Nemesis_IO_Helper(*this)),
+ nemhelper(new Nemesis_IO_Helper(*this, false, single_precision)),
#endif
_timestep(1),
_verbose (false),
@@ -1264,45 +1264,52 @@ void Nemesis_IO::write_nodal_data (const std::string& base_filename,
std::string nemesis_filename = nemhelper->construct_nemesis_filename(base_filename);
if (!nemhelper->opened_for_writing)
+ {
+ // If we're appending, open() the file with read_only=false,
+ // otherwise create() it and write the contents of the mesh to
+ // it.
+ if (_append)
{
- // If we're appending, open() the file with read_only=false,
- // otherwise create() it and write the contents of the mesh to
- // it.
- if (_append)
- {
- nemhelper->open(nemesis_filename.c_str(), /*read_only=*/false);
- // After opening the file, read the header so that certain
- // fields, such as the number of nodes and the number of
- // elements, are correctly initialized for the subsequent
- // call to write the nodal solution.
- nemhelper->read_header();
- }
- else
- {
- nemhelper->create(nemesis_filename);
- nemhelper->initialize(nemesis_filename,mesh);
- nemhelper->write_nodal_coordinates(mesh);
- nemhelper->write_elements(mesh);
- nemhelper->write_nodesets(mesh);
- nemhelper->write_sidesets(mesh);
-
- if( (mesh.boundary_info->n_edge_conds() > 0) &&
- _verbose )
- {
- libMesh::out << "Warning: Mesh contains edge boundary IDs, but these "
- << "are not supported by the ExodusII format."
- << std::endl;
- }
-
- // If we don't have any nodes written out on this processor,
- // Exodus seems to like us better if we don't try to write out any
- // variable names too...
- nemhelper->initialize_nodal_variables(names);
- }
+ nemhelper->open(nemesis_filename.c_str(), /*read_only=*/false);
+ // After opening the file, read the header so that certain
+ // fields, such as the number of nodes and the number of
+ // elements, are correctly initialized for the subsequent
+ // call to write the nodal solution.
+ nemhelper->read_header();
}
+ else
+ {
+ nemhelper->create(nemesis_filename);
+ nemhelper->initialize(nemesis_filename,mesh);
+ nemhelper->write_nodal_coordinates(mesh);
+ nemhelper->write_elements(mesh);
+ nemhelper->write_nodesets(mesh);
+ nemhelper->write_sidesets(mesh);
+
+ if( (mesh.boundary_info->n_edge_conds() > 0) &&
+ _verbose )
+ {
+ libMesh::out << "Warning: Mesh contains edge boundary IDs, but these "
+ << "are not supported by the ExodusII format."
+ << std::endl;
+ }
- nemhelper->write_nodal_solution(soln, names, _timestep);
+ // If we don't have any nodes written out on this processor,
+ // Exodus seems to like us better if we don't try to write out any
+ // variable names too...
+#ifdef LIBMESH_USE_COMPLEX_NUMBERS
+
+ std::vector<std::string> complex_names = nemhelper->get_complex_names(names);
+
+ nemhelper->initialize_nodal_variables(complex_names);
+#else
+ nemhelper->initialize_nodal_variables(names);
+#endif
+ }
+ }
+ nemhelper->write_nodal_solution(soln, names, _timestep);
+
STOP_LOG("write_nodal_data()", "Nemesis_IO");
}
@@ -1337,10 +1344,50 @@ void Nemesis_IO::write_global_data (const std::vector<Number>& soln,
<< std::endl;
libmesh_error();
}
+#ifdef LIBMESH_USE_COMPLEX_NUMBERS
+
+ std::vector<std::string> complex_names = nemhelper->get_complex_names(names);
+
+ nemhelper->initialize_global_variables(complex_names);
+
+ unsigned int num_values = soln.size();
+ unsigned int num_vars = names.size();
+ unsigned int num_elems = num_values / num_vars;
+
+ // This will contain the real and imaginary parts and the magnitude
+ // of the values in soln
+ std::vector<Real> complex_soln(3*num_values);
+
+ for(unsigned i(0); i < num_vars; ++i)
+ {
+
+ for(unsigned int j(0); j < num_elems; ++j)
+ {
+ Number value = soln[i*num_vars + j];
+ complex_soln[3*i*num_elems + j] = value.real();
+ }
+ for(unsigned int j(0); j < num_elems; ++j)
+ {
+ Number value = soln[i*num_vars + j];
+ complex_soln[3*i*num_elems + num_elems +j] = value.imag();
+ }
+ for(unsigned int j(0); j < num_elems; ++j)
+ {
+ Number value = soln[i*num_vars + j];
+ complex_soln[3*i*num_elems + 2*num_elems + j] = std::abs(value);
+ }
+ }
+
+ nemhelper->write_global_values(complex_soln, _timestep);
+
+#else
// Call the Exodus writer implementation
nemhelper->initialize_global_variables( names );
nemhelper->write_global_values( soln, _timestep);
+
+#endif
+
}
#else
View
72 src/mesh/nemesis_io_helper.C
@@ -40,8 +40,8 @@ namespace libMesh
// flag set to false, so that we can make use of its functionality
// on multiple processors.
Nemesis_IO_Helper::Nemesis_IO_Helper(const ParallelObject &parent,
- bool verbose_in) :
- ExodusII_IO_Helper(parent, verbose_in, /*run_only_on_proc0=*/false),
+ bool verbose_in, bool single_precision) :
+ ExodusII_IO_Helper(parent, verbose_in, /*run_only_on_proc0=*/false, /*single_precision=*/single_precision),
nemesis_err_flag(0),
num_nodes_global(0),
num_elems_global(0),
@@ -710,8 +710,18 @@ void Nemesis_IO_Helper::create(std::string filename)
{
// Fall back on double precision when necessary since ExodusII
// doesn't seem to support long double
- int comp_ws = libmesh_cast_int<int>(std::min(sizeof(Real),sizeof(double)));
- int io_ws = libmesh_cast_int<int>(std::min(sizeof(Real),sizeof(double)));
+ int comp_ws(0), io_ws(0);
+
+ if(_single_precision)
+ {
+ comp_ws = sizeof(float);
+ io_ws = sizeof(float);
+ }
+ else
+ {
+ comp_ws = libmesh_cast_int<int>(std::min(sizeof(Real), sizeof(double)));
+ io_ws = libmesh_cast_int<int>(std::min(sizeof(Real), sizeof(double)));
+ }
this->ex_id = exII::ex_create(filename.c_str(), EX_CLOBBER, &comp_ws, &io_ws);
@@ -2304,23 +2314,38 @@ void Nemesis_IO_Helper::write_nodal_coordinates(const MeshBase & mesh)
}
if (local_num_nodes)
+ {
+ if(_single_precision)
+ {
+ std::vector<float> x_single(local_num_nodes), y_single(local_num_nodes), z_single(local_num_nodes);
+ for(unsigned int i(0); i < local_num_nodes; ++i)
+ {
+ x_single[i] = static_cast<float>(x[i]);
+ y_single[i] = static_cast<float>(y[i]);
+ z_single[i] = static_cast<float>(z[i]);
+ }
+
+ ex_err = exII::ex_put_coord(ex_id, &x_single[0], &y_single[0], &z_single[0]);
+ }
+ else
{
// Call Exodus API to write nodal coordinates...
ex_err = exII::ex_put_coord(ex_id, &x[0], &y[0], &z[0]);
- EX_CHECK_ERR(ex_err, "Error writing node coordinates");
-
- // And write the nodal map we created for them
- ex_err = exII::ex_put_node_num_map(ex_id, &(this->exodus_node_num_to_libmesh[0]));
- EX_CHECK_ERR(ex_err, "Error writing node num map");
}
+ EX_CHECK_ERR(ex_err, "Error writing node coordinates");
+
+ // And write the nodal map we created for them
+ ex_err = exII::ex_put_node_num_map(ex_id, &(this->exodus_node_num_to_libmesh[0]));
+ EX_CHECK_ERR(ex_err, "Error writing node num map");
+ }
else // Does the Exodus API want us to write empty nodal coordinates?
- {
- ex_err = exII::ex_put_coord(ex_id, NULL, NULL, NULL);
- EX_CHECK_ERR(ex_err, "Error writing empty node coordinates");
+ {
+ ex_err = exII::ex_put_coord(ex_id, NULL, NULL, NULL);
+ EX_CHECK_ERR(ex_err, "Error writing empty node coordinates");
- ex_err = exII::ex_put_node_num_map(ex_id, NULL);
- EX_CHECK_ERR(ex_err, "Error writing empty node num map");
- }
+ ex_err = exII::ex_put_node_num_map(ex_id, NULL);
+ EX_CHECK_ERR(ex_err, "Error writing empty node num map");
+ }
}
@@ -2400,6 +2425,22 @@ void Nemesis_IO_Helper::write_nodal_solution(const std::vector<Number> & values,
for (int c=0; c<num_vars; c++)
{
+#ifdef LIBMESH_USE_COMPLEX_NUMBERS
+ std::vector<Real> real_parts(num_nodes);
+ std::vector<Real> imag_parts(num_nodes);
+ std::vector<Real> magnitudes(num_nodes);
+
+ for(int i(0); i < num_nodes; ++i)
+ {
+ Number value = values[this->exodus_node_num_to_libmesh[i]*num_vars + c];
+ real_parts[i] = value.real();
+ imag_parts[i] = value.imag();
+ magnitudes[i] = std::abs(value);
+ }
+ write_nodal_values(3*c+1,real_parts,timestep);
+ write_nodal_values(3*c+2,imag_parts,timestep);
+ write_nodal_values(3*c+3,magnitudes,timestep);
+#else
std::vector<Number> cur_soln(num_nodes);
//Copy out this variable's solution
@@ -2407,6 +2448,7 @@ void Nemesis_IO_Helper::write_nodal_solution(const std::vector<Number> & values,
cur_soln[i] = values[this->exodus_node_num_to_libmesh[i]*num_vars + c];
write_nodal_values(c+1,cur_soln,timestep);
+#endif
}
}
Something went wrong with that request. Please try again.