我正在为特定的流体问题编写CFD解算器。到目前为止,每次运行模拟时都会生成网格,并且当更改几何形状和流体属性时,需要重新编译程序。
对于单元数较少的小型问题,它的工作原理很好。但是对于细胞数超过一百万的情况,需要经常更改流体性质,效率很低。
显然,我们需要将模拟设置数据存储在配置文件中,并将几何信息存储在格式化的网格文件中。
% Dimension: 2D or 3D
N_Dimension= 2
% Number of fluid phases
N_Phases= 1
% Fluid density (kg/m3)
Density_Phase1= 1000.0
Density_Phase2= 1.0
% Kinematic viscosity (m^2/s)
Viscosity_Phase1= 1e-6
Viscosity_Phase2= 1.48e-05
...
% Dimension: 2D or 3D
N_Dimension= 2
% Points (index: x, y, z)
N_Points: 100
x0 y0
x1 y1
...
x99 y99
% Faces (Lines in 2D: P1->p2)
N_Faces: 55
0 2
3 4
...
% Cells (polygons in 2D: Cell-Type and Points clock-wise). 6: triangle; 9: quad
N_Cells: 20
9 0 1 6 20
9 1 3 4 7
...
% Boundary Faces (index)
Left_Faces: 4
0
1
2
3
Bottom_Faces: 6
7
8
9
10
11
12
...
很容易将配置和网格信息写入格式化的文本文件。问题是,如何将这些数据有效地读入程序?我想知道是否有任何易于使用的c ++库来完成这项工作。
答案 0 :(得分:5)
好吧 您可以基于有限元素集合,字典,一些Regex来实现自己的API,并且毕竟可以根据一些国际标准进行投注实践。
或者您可以看一下:
我刚刚在C ++ OpenGL项目的上一个实现中使用了OpenMesh。
答案 1 :(得分:5)
作为仅需获得可容忍的东西的第一个迭代解决方案,请使用@JosmarBarbosa的suggestion并为您的数据使用已建立的格式-它可能还具有免费的开放源代码库供您使用。一个例子是亚琛工业大学开发的OpenMesh。它支持:
- 任意多边形(一般情况)和纯三角形网格的表示(提供更有效的专用算法)
- 顶点,半边,边缘和面的明确表示。
- 快速访问社区,尤其是单环社区(请参见下文)。
- [自定义]
但是如果您真的需要加快网格数据的读取速度,请考虑执行以下操作:
最后,您可以避免完全显式读取,而对每个数据文件使用内存映射。见
fastest technique to read a file into memory?
说明/腔室
:答案 2 :(得分:4)
在以下假设中,我假设:
while ( (output = inputStream.readLine()) != null) {
println("Debug: " + output)
}
,则它将作为注释忽略。%
。我提供的代码将按照上述假设正确解析配置文件。这是代码(我希望所有需要的解释都在注释中):
identifier= value
阅读标识符后,您当然可以对它们进行任何处理。我仅以打印它们为例来说明如何获取它们。有关#include <fstream> //required for file IO
#include <iostream> //required for console IO
#include <unordered_map> //required for creating a hashtable to store the identifiers
int main()
{
std::unordered_map<std::string, double> identifiers;
std::string configPath;
std::cout << "Enter config path: ";
std::cin >> configPath;
std::ifstream config(configPath); //open the specified file
if (!config.is_open()) //error if failed to open file
{
std::cerr << "Cannot open config file!";
return -1;
}
std::string line;
while (std::getline(config, line)) //read each line of the file
{
if (line[0] == '%') //line is a comment
continue;
std::size_t identifierLenght = 0;
while (line[identifierLenght] != '=')
++identifierLenght;
identifiers.emplace(
line.substr(0, identifierLenght),
std::stod(line.substr(identifierLenght + 2))
); //add entry to identifiers
}
for (const auto& entry : identifiers)
std::cout << entry.first << " = " << entry.second << '\n';
}
的更多信息,请查看here。有关使解析器有很多非常好的信息,请改用here。
如果要使程序处理输入更快,请在std::unordered_map
的开头插入以下行:main
。这将使C ++ IO与C IO失去同步,从而使其变得更快。
答案 3 :(得分:4)
假设:
简而言之,您确实需要自己的文本格式。
您可以使用任何parser generator入门。虽然您可能只使用正则表达式来解析配置文件,但从长远来看,它们可能确实受到限制。因此,我建议使用context-free grammar生成的Boost spirit::x3解析器。
抽象语法树将保存解析器的最终结果。
#include <string>
#include <utility>
#include <vector>
#include <variant>
namespace AST {
using Identifier = std::string; // Variable name.
using Value = std::variant<int,double>; // Variable value.
using Assignment = std::pair<Identifier,Value>; // Identifier = Value.
using Root = std::vector<Assignment>; // Whole file: all assignments.
}
语法描述:
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/spirit/home/x3.hpp>
namespace Parser {
using namespace x3;
// Line: Identifier = value
const x3::rule<class assignment, AST::Assignment> assignment = "assignment";
// Line: comment
const x3::rule<class comment> comment = "comment";
// Variable name
const x3::rule<class identifier, AST::Identifier> identifier = "identifier";
// File
const x3::rule<class root, AST::Root> root = "root";
// Any valid value in the config file
const x3::rule<class value, AST::Value> value = "value";
// Semantic action
auto emplace_back = [](const auto& ctx) {
x3::_val(ctx).emplace_back(x3::_attr(ctx));
};
// Grammar
const auto assignment_def = skip(blank)[identifier >> '=' >> value];
const auto comment_def = '%' >> omit[*(char_ - eol)];
const auto identifier_def = lexeme[alpha >> +(alnum | char_('_'))];
const auto root_def = *((comment | assignment[emplace_back]) >> eol) >> omit[*blank];
const auto value_def = double_ | int_;
BOOST_SPIRIT_DEFINE(root, assignment, comment, identifier, value);
}
// Takes iterators on string/stream...
// Returns the AST of the input.
template<typename IteratorType>
AST::Root parse(IteratorType& begin, const IteratorType& end) {
AST::Root result;
bool parsed = x3::parse(begin, end, Parser::root, result);
if (!parsed || begin != end) {
throw std::domain_error("Parser received an invalid input.");
}
return result;
}
x3::skip(blank)
表达式中添加/移动xxxx_def
。root_def
表达式可以解决此问题。您仅需几个规则即可解析更复杂的内容:
// 100 X_n Y_n
const auto point_def = lit("N_Points") >> ':' >> int_ >> eol >> *(double_ >> double_ >> eol)
答案 4 :(得分:2)
如果您不需要特定的文本文件格式,但是有大量数据并且确实关心性能,建议您改用一些现有的数据序列化框架。
例如Google协议缓冲区允许使用很少的代码进行有效的序列化和反序列化。该文件是二进制文件,因此通常比文本文件小得多,并且二进制序列化比解析文本要快得多。它还支持结构化数据(数组,嵌套结构),数据版本控制和其他功能。