Commit 854e24f3 authored by Fabian.Wein's avatar Fabian.Wein
Browse files

Integrate the openCFS hdf5 reader as CFSReader plugin

* is a modification of the plugin from gitlab.com/openCFS
* has a dependency hdf5 with c and hl api and boost filesystem
* reads hdf5 files with openCFS data format (usually called *.cfs)
* knows multiple regions, multiple steps and multiple solution types
* real and complex valued data
* used C++11 w/o parallelization
parent 51058673
Pipeline #282185 failed with stages
......@@ -259,6 +259,7 @@ set(paraview_default_plugins
AnalyzeNIfTIReaderWriter
ArrowGlyph
BagPlotViewsAndFilters
CFSReader
Datamine
DigitalRockPhysics
EULATestPlugin
......
# There is now a plugin to load openCFS data in ParaView.
openCFS is an open source academic coupled field FEM simulation and
structural optimization tool (opencfs.org).
The data format of openCFS is hdf5 based and has the extension .cfs, .nmf, .nrf, .h5ref
paraview_add_plugin(CFSReader
REQUIRED_ON_SERVER
VERSION "22.04"
MODULES CFSReaderModules
MODULE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/Reader/vtk.module")
MIT License
Copyright (c) 2020 Verein zur Förderung der Software openCFS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ParaView License Version 1.2
========================================================================
Copyright (c) 2005-2008 Sandia Corporation, Kitware Inc.
Sandia National Laboratories, New Mexico
PO Box 5800
Albuquerque, NM 87185
Kitware Inc.
28 Corporate Drive
Clifton Park, NY 12065
USA
Under the terms of Contract DE-AC04-94AL85000, there is a
non-exclusive license for use of this work by or on behalf of the
U.S. Government.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the
distribution.
* Neither the name of Kitware nor the names of any contributors may
be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# CFSReader plugin
ParaView/VTK reader plugin for openCFS data files.
openCFS is an academic based FEM and structural optimization software under MIT/X11 license: opencfs.org
The data files are hdf5 based and called .cfs, .h5ref, .nmf and .nrf.
Theses are only different extensions for the same file structure.
Reasons for so many extensions: reference files for testing, forks, legacy
Dependencies: hdf5 (c-api and c-HL) via vtk
## License
The CFSReader reader plugin is copyright Verein zur Foerderung der Software openCFS
and under both the Paraview BSD licence http://www.paraview.org/HTML/Copyright.html and the Verein zur Foerderung der Software openCFS MIT/X11 license, see LICENSE file in this directory.
## Contact Info:
On any issue, contact https://gitlab.kitware.com/fabian.wein.
Alternatively contact openCFS developers at https://gitlab.com/openCFS/cfs
<ServerManagerConfiguration>
<ProxyGroup name="sources">
<SourceProxy name="CFSReader"
class="vtkCFSReader"
label="openCFS HDF5 reader">
<Documentation
short_help="Read an openCFS HDF5 file"
long_help="Read an openCFS result file in HDF5 encoding.">
openCFS stores its data in HDF5 format (www.hdfgroup.org).
</Documentation>
<StringVectorProperty
name="FileName"
animateable="0"
command="SetFileName"
number_of_elements="1">
<FileListDomain name="files"/>
<Documentation>
Name of the openCFS HDF5 reader.
</Documentation>
</StringVectorProperty>
<!-- General section do declare valid file extensions and to make 'hidden' properties visible -->
<Hints>
<ReaderFactory extensions="cfs h5ref nrf nmf"
file_description="openCFS Result/Mesh Files" />
</Hints>
<!-- ==================================== -->
<!-- Region Selection Widget -->
<!-- ==================================== -->
<StringVectorProperty name="RegionArrayInfo" information_only="1">
<ArraySelectionInformationHelper attribute_name="Region"/>
</StringVectorProperty>
<StringVectorProperty name="RegionArrayStatus"
command="SetRegionArrayStatus"
number_of_elements="0"
repeat_command="1"
number_of_elements_per_command="2"
element_types="2 0"
information_property="RegionArrayInfo"
label="Regions">
<ArraySelectionDomain name="array_list">
<RequiredProperties>
<Property name="RegionArrayInfo" function="RegionList"/>
</RequiredProperties>
</ArraySelectionDomain>
<Documentation>
Select which regions to read in.
</Documentation>
</StringVectorProperty>
<!-- ==================================== -->
<!-- Element Group Selection Widget -->
<!-- ==================================== -->
<StringVectorProperty name="NamedElemArrayInfo" information_only="1">
<ArraySelectionInformationHelper attribute_name="NamedElem"/>
</StringVectorProperty>
<StringVectorProperty name="NamedElemArrayStatus"
command="SetNamedElemArrayStatus"
number_of_elements="0"
repeat_command="1"
number_of_elements_per_command="2"
element_types="2 0"
information_property="NamedElemArrayInfo"
label="Element Groups">
<ArraySelectionDomain name="array_list">
<RequiredProperties>
<Property name="NamedElemArrayInfo" function="NamedElemList"/>
</RequiredProperties>
</ArraySelectionDomain>
<Documentation>
Select which element groups to read in.
</Documentation>
</StringVectorProperty>
<!-- ==================================== -->
<!-- Nodal Group Selection Widget -->
<!-- ==================================== -->
<StringVectorProperty name="NamedNodeArrayInfo"
information_only="1">
<ArraySelectionInformationHelper attribute_name="NamedNode"/>
</StringVectorProperty>
<StringVectorProperty name="NamedNodeArrayStatus"
command="SetNamedNodeArrayStatus"
number_of_elements="0"
repeat_command="1"
number_of_elements_per_command="2"
element_types="2 0"
information_property="NamedNodeArrayInfo"
label="Nodal Groups">
<ArraySelectionDomain name="array_list">
<RequiredProperties>
<Property name="NamedNodeArrayInfo" function="NamedNodeList"/>
</RequiredProperties>
</ArraySelectionDomain>
<Documentation>
Select which nodal groups to read in.
</Documentation>
</StringVectorProperty>
<!-- ==================================== -->
<!-- Select Analysis Step -->
<!-- ==================================== -->
<IntVectorProperty name="AnalysisStep"
command="SetMultiSequenceStep"
number_of_elements="1"
animateable="1"
default_values="1">
<IntRangeDomain name="range">
<RequiredProperties>
<Property name="MultiSequenceRangeInfo" function="Range"/>
</RequiredProperties>
</IntRangeDomain>
<Documentation>
Select the current analysis step.
</Documentation>
</IntVectorProperty>
<!-- Get time/freq range of current multisequence step -->
<IntVectorProperty name="TimeStepNumRangeInfo"
command="GetTimeStepNumRange"
number_of_elements="2"
information_only="1"
default_values="1 1">
<SimpleIntInformationHelper/>
</IntVectorProperty>
<!-- ==================================== -->
<!-- Switch: Harmonic Mode Shaope -->
<!-- ==================================== -->
<IntVectorProperty
name="InterpretHarmonicDataAsModeShape"
command="SetHarmonicDataAsModeShape"
number_of_elements="1"
default_values="0"
label="Animate Harmonic / Modal Results"
animateable="0">
<BooleanDomain name="bool"/>
<Documentation>
By switching on this flag, the reader will generate continuous transient data of one
period of the harmonic oscillation. There will be a time range [0, 1]
corresponding to an interval [0, 2*pi] for the argument of the rotating phasor.
</Documentation>
</IntVectorProperty>
<!-- ==================================== -->
<!-- Select Frequency Step -->
<!-- ==================================== -->
<IntVectorProperty name="TimeStep"
command="SetTimeStep"
number_of_elements="1"
animateable="1"
default_values="0"
label="Frequency Step">
<!--information_property="TimestepValues"-->
<IntRangeDomain name="range">
<RequiredProperties>
<Property name="TimeStepNumRangeInfo" function="Range"/>
</RequiredProperties>
</IntRangeDomain>
<Documentation>
Select the step to aniamte over one oscillation period.
</Documentation>
</IntVectorProperty>
<!-- ==================================== -->
<!-- Switch: Complex Mode -->
<!-- ==================================== -->
<IntVectorProperty
name="ComplexPartReal"
command="SetComplexReal"
number_of_elements="1"
default_values="1"
label="Real"
animateable="0">
<BooleanDomain name="bool"/>
<Documentation>
Provide real part of complex valued result (called *Real)
</Documentation>
</IntVectorProperty>
<IntVectorProperty
name="ComplexPartImag"
command="SetComplexImag"
number_of_elements="1"
default_values="1"
label="Imag"
animateable="0">
<BooleanDomain name="bool"/>
<Documentation>
Provide imaginary part of complex valued result (called *Imag)
</Documentation>
</IntVectorProperty>
<IntVectorProperty
name="ComplexPartAmpl"
command="SetComplexAmpl"
number_of_elements="1"
default_values="0"
label="Amplitude"
animateable="0">
<BooleanDomain name="bool"/>
<Documentation>
Provide amplitude of complex valued result (called *Ampl)
</Documentation>
</IntVectorProperty>
<IntVectorProperty
name="ComplexPartPhase"
command="SetComplexPhase"
number_of_elements="1"
default_values="0"
label="Phase"
animateable="0">
<BooleanDomain name="bool"/>
<Documentation>
Provide phase of complex valued result (called *Phase)
</Documentation>
</IntVectorProperty>
<PropertyGroup panel_widget="Line" label="Complex Parts">
<Property function="SetComplexReal" name="ComplexPartReal" />
<Property function="SetComplexImag" name="ComplexPartImag" />
<Property function="SetComplexAmpl" name="ComplexPartAmpl" />
<Property function="SetComplexPhase" name="ComplexPartPhase" />
</PropertyGroup>
<!-- Get number of multisequence steps -->
<IntVectorProperty name="MultiSequenceRangeInfo"
command="GetMultiSequenceRange"
number_of_elements="2"
information_only="1"
default_values="1 1">
<SimpleIntInformationHelper/>
</IntVectorProperty>
<!-- ==================================== -->
<!-- Switch: Fill Missing Resuls -->
<!-- ==================================== -->
<IntVectorProperty
name="FillMissingResults"
command="SetFillMissingResults"
number_of_elements="1"
label = "Fill Missing Results"
default_values="0" >
<BooleanDomain name="bool"/>
<Documentation>
If activated, results, which are just defined for some regions, will be
filled with zeros on the remaining regions.
It will require more memory, but prevents problem e.g. with StreamTracer.
</Documentation>
</IntVectorProperty>
<!-- ==================================== -->
<!-- Switch: Add Dimensional Units -->
<!-- ==================================== -->
<IntVectorProperty
name="AddDimensionsToArrayNames"
command="SetAddDimensionsToArrayNames"
number_of_elements="1"
default_values="0"
label="Add Dimensional Units To Results"
animateable="0">
<BooleanDomain name="bool"/>
<Documentation>
Read dimensional units from field data and add them to array names as human-readable string.
</Documentation>
</IntVectorProperty>
<!-- ==================================== -->
<!-- Show Current Frequency Value -->
<!-- ==================================== -->
<StringVectorProperty name="TimeFreqVal"
number_of_elements="1"
command="GetTimeFreqValStr"
information_only="1"
panel_visibility="default"
animateable="1"
default_values="0.0"
label="Frequency">
<Documentation>
Current time / frequency value.
</Documentation>
</StringVectorProperty>
<!-- This property is used by the property panel to show the available timesteps in the data. -->
<DoubleVectorProperty name="TimestepValues"
repeatable="1"
information_only="1">
<TimeStepsInformationHelper />
<Documentation>
Choose time step (frequency step/ eigenvector) for
transient animation via ParaView.
</Documentation>
</DoubleVectorProperty>
<DoubleVectorProperty name="TimeRange" information_only="1">
<TimeRangeInformationHelper/>
<Documentation>
Sends time range information to the animation panel.
ParaView uses this information to set the range of time
for the animation.
</Documentation>
</DoubleVectorProperty>
<!-- End CFSReader -->
</SourceProxy>
</ProxyGroup>
</ServerManagerConfiguration>
# the plugin uses hdf5 c-api and hl-api via vtk
vtk_module_add_module(CFSReaderModules
CLASSES vtkCFSReader
SOURCES hdf5Common.cc hdf5Reader.cc
PRIVATE_HEADERS hdf5Common.h hdf5Reader.h)
paraview_add_server_manager_xmls(XMLS CFSReader.xml)
<
/*=========================================================================
Program: ParaView
Module: hdf5Common.cc
Copyright (c) Verein zur Foerderung der Software openCFS
All rights reserved.
ParaView is a free software; you can redistribute it and/or modify it
under the terms of the ParaView license version 1.2.
See License_v1.2.txt for the full ParaView license.
A copy of this license can be obtained by contacting
Kitware Inc.
28 Corporate Drive
Clifton Park, NY 12065
USA
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
=========================================================================*/
#include "hdf5Common.h"
#include <cassert>
namespace H5CFS
{
std::string GetObjNameByIdx(hid_t loc, hsize_t idx)
{
// We first obtain the length of the name, then by the same function write the name to a buffer
// and third copy it to a std::string we return. There is no shortcut possible
// nullptr for name gives the length
ssize_t name_len = H5Lget_name_by_idx(loc, ".", H5_INDEX_NAME, H5_ITER_NATIVE, idx, nullptr, 0, 0);
if(name_len < 0)
{
throw std::runtime_error("Was not able to determine name" );
}
// now, allocate buffer to get the name, we may not write to std::string data directly. std::make_unique is not C++11
// https://stackoverflow.com/questions/1042940/writing-directly-to-stdstring-internal-buffers
std::vector<char> buf(name_len+1); // the trailing NULL is written but not counted
if(H5Lget_name_by_idx(loc, ".", H5_INDEX_NAME, H5_ITER_NATIVE, idx, buf.data(), name_len+1,0) < 0)
{
throw std::runtime_error(std::string("error obtaining obj name with index ") + std::to_string(idx) + " of size " + std::to_string(name_len));
}
std::string name(buf.data());
return name;
}
hid_t OpenGroup(hid_t loc, const std::string& name, bool throw_exception)
{
hid_t ret = H5Gopen(loc, name.c_str(), H5P_DEFAULT);
if(ret < 0 && throw_exception)
{
throw std::runtime_error("cannot open group '" + name + "'");
}
return ret;
}
hid_t OpenDataSet(hid_t loc, const std::string& name)
{
hid_t ret = H5Dopen(loc, name.c_str(), H5P_DEFAULT);
if(ret < 0)
{
throw std::runtime_error("cannot open group '" + name + "'");
}
return ret;
}
H5G_info_t GetInfo(hid_t group_id)
{
H5G_info_t info;
if(H5Gget_info(group_id, &info) < 0)
{
throw std::runtime_error("cannot get group info");
}
return info;
}
int GetDataSetSize(hid_t loc_id, const char *name)
{
int val = 0;
if(H5LTget_dataset_ndims(loc_id, name, &val) < 0)
{
throw std::runtime_error("cannot get size of data set '" + std::string(name) + "'");
}
return val;
}
template<>
void ReadAttribute<int>(hid_t loc_id, const std::string& obj_name, const std::string& attr_name, int& data)
{
if(H5LTget_attribute_int(loc_id, obj_name.c_str(), attr_name.c_str(), &data) < 0)
{
throw std::runtime_error(std::string("cannot read int attribute ") + obj_name + "/" + attr_name);
}
}
template<>
void ReadAttribute<unsigned int>(hid_t loc_id, const std::string& obj_name, const std::string& attr_name, unsigned int& data)
{
if(H5LTget_attribute_uint(loc_id, obj_name.c_str(), attr_name.c_str(), &data) < 0)
{
throw std::runtime_error(std::string("cannot read uint attribute ") + obj_name + "/" + attr_name);
}
}
template<>
void ReadAttribute<double>(hid_t loc_id, const std::string& obj_name, const std::string& attr_name, double& data)
{
if(H5LTget_attribute_double(loc_id, obj_name.c_str(), attr_name.c_str(), &data) < 0)
{
throw std::runtime_error(std::string("cannot read double attribute ") + obj_name + "/" + attr_name);
}
}
template<>
void ReadAttribute<std::string>(hid_t loc_id, const std::string& obj_name, const std::string& attr_name, std::string& data)
{
// H5LTget_attribute_string is a very poorly described function
// it has a char* data with OUT: Buffer with data.
// but it needs the address of the pointer and it is not said who manages the buffer?!
// we follow stackoverflow but need a type cast
// https://stackoverflow.com/questions/64467420/how-to-use-the-h5ltget-attribute-string-function
char* out = nullptr;
if(H5LTget_attribute_string(loc_id, obj_name.c_str(), attr_name.c_str(), reinterpret_cast<char*>(&out)) < 0) // see comment for cast
{
throw std::runtime_error(std::string("cannot obtain string attribute value for ") + obj_name + "/" + attr_name);
}
data = std::string(out);
}
std::vector<unsigned int> GetArrayDims(hid_t loc, const std::string& name)
{
hid_t set = OpenDataSet(loc, name);
hid_t space_id = H5Dget_space(set);
if(H5Sis_simple(space_id) <= 0)
{
throw std::runtime_error(std::string("no simple data space ") + name);
}
const int ndims = H5Sget_simple_extent_ndims(space_id);
assert(ndims > 0);
std::vector<hsize_t> dims(ndims);
if(H5Sget_simple_extent_dims(space_id, dims.data(), nullptr) != ndims) // nullptr means no max dims
{
throw std::runtime_error(std::string("read dimensions not as expected for ") + name);
}
H5Sclose(space_id);
H5Dclose(set);
std::vector<unsigned int> ret(ndims);
for(int i = 0; i < ndims; i++)
{
ret[i] = static_cast<unsigned int>(dims[i]); // dims is long long, so we cannot directly write to reg.data()
}
return ret;
}