.gitignore 0 → 100644
build
\ No newline at end of file
.gitlab-ci.yml 0 → 100644
image: mwm126/mfix-exa:cuda
variables:
GIT_STRATEGY: fetch
GIT_SUBMODULE_STRATEGY: normal
###### Test jobs #####
test:all:
script:
- cmake -S .
-B build
-G Ninja
- cmake --build build --target unit_tests
- ./build/src/tests/unit_tests
- cmake --build build --target mfix-parser
- find -name inputs
- find -name inputs |xargs -l ./build/mfix-parser
tags:
- mfix-exa
.gitmodules 0 → 100644
[submodule "PEGTL"]
path = subprojects/PEGTL
url = https://github.com/taocpp/PEGTL
[submodule "mfix/subprojects/mfix"]
path = subprojects/mfix
url = ../mfix
[submodule "subprojects/Catch2"]
path = subprojects/Catch2
url = https://github.com/catchorg/Catch2
CMakeLists.txt 0 → 100644
cmake_minimum_required(VERSION 3.14)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
project(MFIX-Parser
DESCRIPTION "A parser for MFIX-Exa input files"
HOMEPAGE_URL "https://mfix.netl.doe.gov/gitlab/exa/mfix-parser"
LANGUAGES CXX
)
set( CMAKE_EXPORT_COMPILE_COMMANDS ON )
set(USE_CCACHE "")
find_program(CCACHE_FOUND ccache)
if(CCACHE_FOUND)
set( CMAKE_CXX_COMPILER_LAUNCHER ccache )
endif()
add_subdirectory(src)
set(EXENAME "mfix-parser")
add_executable(${EXENAME} src/main.cpp)
target_link_libraries(${EXENAME} parser)
# mfix-parser
Repo for parsing and validating MFIX input(s) files for use by mfix-app
\ No newline at end of file
Repo for parsing and validating MFIX input(s) files for use by mfix-app.
Uses PEGTL for parsing and Catch2 for tests.
This has the MFIX-Exa repo as a submodule in order to test against existing ``inputs`` files.
## Build
> python3 pip install cmake # if CMake not installed
> cmake -S. -Bbuild
> cmake --build build
> cd build
> ctest
## Run Tests
> cmake --build build --target unit_tests
> ./build/src/tests/unit_tests
## Run on inputs file
> cmake --build build --target mfix-parser
> ./build/mfix-parser subprojects/mfix/benchmarks/01-HCS/Size0001/inputs && echo "Parsing succeeded."
################################################################################
# inputs Parser
################################################################################
add_library(parser
parser.cpp
)
target_include_directories(parser PRIVATE
${CMAKE_SOURCE_DIR}/subprojects/PEGTL/include)
target_link_libraries(parser PRIVATE stdc++fs)
add_subdirectory(tests)
find_program(CLANG_FMT NAMES clang-format-9 clang-format-8 clang-format-7 clang-format)
if(CLANG_FMT)
get_target_property(_psrcs unit_tests SOURCES)
add_custom_target(fmt_test
COMMAND ${CLANG_FMT} -i ${_psrcs}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests
)
file(GLOB _includes ${PROJECT_SOURCE_DIR}/src/*.hpp)
add_custom_target(fmt)
add_dependencies(fmt fmt_test)
endif()
src/double.hpp 0 → 100644
// Copyright (c) 2014-2019 Dr. Colin Hirsch and Daniel Frey
// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
#ifndef TAO_PEGTL_SRC_EXAMPLES_PEGTL_DOUBLE_HPP
#define TAO_PEGTL_SRC_EXAMPLES_PEGTL_DOUBLE_HPP
#include <tao/pegtl.hpp>
namespace double_ {
// A grammar for doubles suitable for std::stod without locale support.
// See also: http://en.cppreference.com/w/cpp/string/basic_string/stof
using namespace TAO_PEGTL_NAMESPACE;
// clang-format off
struct plus_minus : opt< one< '+', '-' > > {};
struct dot : one< '.' > {};
struct inf : seq< istring< 'i', 'n', 'f' >,
opt< istring< 'i', 'n', 'i', 't', 'y' > > > {};
struct nan : seq< istring< 'n', 'a', 'n' >,
opt< one< '(' >,
plus< alnum >,
one< ')' > > > {};
template< typename D >
struct number : if_then_else< dot,
plus< D >,
seq< plus< D >, opt< dot, star< D > > > > {};
struct e : one< 'e', 'E' > {};
struct p : one< 'p', 'P' > {};
struct exponent : seq< plus_minus, plus< digit > > {};
struct decimal : seq< number< digit >, opt< e, exponent > > {};
struct hexadecimal : seq< one< '0' >, one< 'x', 'X' >, number< xdigit >, opt< p, exponent > > {};
struct grammar : seq< plus_minus, sor< hexadecimal, decimal, inf, nan > > {};
// clang-format on
} // namespace double_
#endif
src/inputs.hpp 0 → 100644
#ifndef INPUTS_H_
#define INPUTS_H_
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <variant>
#include <vector>
namespace parser {
using InputArray = std::vector<double>;
using InputValue = std::variant<std::string, InputArray>;
using InputInfo = std::map<std::string, InputValue>;
std::optional<InputInfo> parse_inputs(std::string);
} // namespace parser
#endif
src/main.cpp 0 → 100644
#include <fstream>
#include <iostream>
#include <sstream>
#include "inputs.hpp"
std::string read_file(std::string inputs_file) {
std::ifstream in_stream;
in_stream.open(inputs_file.c_str());
std::stringstream str_stream;
str_stream << in_stream.rdbuf();
return str_stream.str();
}
int main(int argc, char *argv[]) {
std::string fname(argv[1]);
auto inputs_str = read_file(fname);
auto maybe_success = parser::parse_inputs(inputs_str);
if (maybe_success.has_value()) {
return 0;
}
return -1;
}
src/parser.cpp 0 → 100644
// standard includes
#include <assert.h>
#include <memory>
// subproject includes
#include <tao/pegtl.hpp>
// includes
#include "double.hpp"
#include "inputs.hpp"
using namespace TAO_PEGTL_NAMESPACE;
namespace {
struct parser_state {
std::string current_key;
std::vector<double> current_vec;
parser::InputValue current_value;
parser::InputInfo info;
};
} // namespace
namespace {
struct escaped_x : seq<one<'x'>, rep<2, must<xdigit>>> {};
struct escaped_u : seq<one<'u'>, rep<4, must<xdigit>>> {};
struct escaped_U : seq<one<'U'>, rep<8, must<xdigit>>> {};
struct escaped_c
: one<'\'', '"', '?', '\\', 'a', 'b', 'f', 'n', 'r', 't', 'v'> {};
struct escaped : sor<escaped_x, escaped_u, escaped_U, escaped_c> {};
struct character
: if_must_else<one<'\\'>, escaped, utf8::range<0x20, 0x10FFFF>> {};
struct string_literal : if_must<one<'"'>, until<one<'"'>, character>> {};
struct implicit_string : if_must<alnum, until<eolf, character>> {};
struct whitespace_delimited_array : list<double_::grammar, plus<one<' '>>> {};
struct L_FUN : pad<string<'('>, space> {};
struct R_FUN : pad<string<')'>, space> {};
struct L_ARR : pad<string<'['>, space> {};
struct R_ARR : pad<string<']'>, space> {};
struct L_BLK : pad<string<'{'>, space> {};
struct R_BLK : pad<string<'}'>, space> {};
struct S_CLN : pad<string<';'>, space> {};
struct name : identifier {};
struct value
: sor<whitespace_delimited_array, string_literal, implicit_string> {};
struct comment : if_must<one<'#'>, until<eolf>> {};
struct key : list<identifier, one<'.'>> {};
struct keyval : seq<pad<key, space>, string<'='>, pad<value, space>> {};
struct grammar : pad<sor<keyval, comment>, space> {};
template <typename Rule> struct action {};
template <> struct action<key> {
template <typename Input>
static void apply(const Input &in, parser_state &st) {
std::stringstream ss(in.string());
ss >> st.current_key;
}
};
template <> struct action<string_literal> {
template <typename Input>
static void apply(const Input &in, parser_state &st) {
std::stringstream ss(in.string());
std::string v;
ss >> v;
st.current_value = v.substr(1, v.length() - 2); // strip quotes
}
};
template <> struct action<implicit_string> {
template <typename Input>
static void apply(const Input &in, parser_state &st) {
std::stringstream ss(in.string());
std::string v;
ss >> v;
st.current_value = v;
}
};
template <> struct action<double_::grammar> {
template <typename Input>
static void apply(const Input &in, parser_state &st) {
std::stringstream ss(in.string());
double v;
ss >> v;
st.current_vec.push_back(v);
}
};
template <> struct action<whitespace_delimited_array> {
template <typename Input>
static void apply(const Input &in, parser_state &st) {
std::stringstream ss(in.string());
st.current_value = st.current_vec;
st.current_vec.clear();
}
};
template <> struct action<keyval> {
template <typename Input>
static void apply(const Input &in, parser_state &st) {
st.info[st.current_key] = st.current_value;
}
};
std::optional<parser_state> do_parse(std::string str) {
parser_state st;
memory_input in(str, "std::cin");
if (!parse<grammar, action>(in, st)) {
return std::nullopt;
}
return st;
}
} // namespace
namespace parser {
std::optional<parser::InputInfo> parse_inputs(std::string str) {
auto maybe_state = do_parse(str);
if (!maybe_state.has_value()) {
return std::nullopt;
}
return maybe_state.value().info;
}
} // namespace parser
################################################################################
# Parser Tests
################################################################################
add_executable(unit_tests EXCLUDE_FROM_ALL
../parser.cpp
parser/main.cpp
parser/inputs.t.cpp
)
target_include_directories(unit_tests PRIVATE
${CMAKE_SOURCE_DIR}/subprojects/PEGTL/include
${CMAKE_SOURCE_DIR}/src
)
SET(catch2_dir ${PROJECT_SOURCE_DIR}/subprojects/Catch2)
find_package(Git QUIET)
if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
# Update submodules as needed
option(GIT_SUBMODULE "Check submodules during build" ON)
if(GIT_SUBMODULE)
message(STATUS "Submodule update")
execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
RESULT_VARIABLE GIT_SUBMOD_RESULT)
if(NOT GIT_SUBMOD_RESULT EQUAL "0")
message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules")
endif()
endif()
endif()
if(NOT EXISTS "${catch2_dir}/CMakeLists.txt")
message(FATAL_ERROR "The submodules were not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.")
endif()
add_subdirectory(${catch2_dir} ${CMAKE_CURRENT_BINARY_DIR}/catch_build)
list(APPEND CMAKE_MODULE_PATH "${catch2_dir}/contrib/")
target_link_libraries(unit_tests Catch2::Catch2)
include(CTest)
include(Catch)
catch_discover_tests(unit_tests)
#include "catch2/catch.hpp"
#include <inputs.hpp>
TEST_CASE("comment", "[]") {
auto maybe_st = parser::parse_inputs(R"(
# this is a comment
)");
CHECK(maybe_st.has_value());
}
TEST_CASE("double_int", "[]") {
auto maybe_st = parser::parse_inputs(R"(
mfix.max_step = 100
)");
REQUIRE(maybe_st.has_value());
auto info = maybe_st.value();
auto value = info["mfix.max_step"];
auto aa = std::get<parser::InputArray>(value);
CHECK(aa.size() == 1);
CHECK(aa[0] == 100);
}
TEST_CASE("double", "[]") {
auto maybe_st = parser::parse_inputs(R"(
mfix.stop_time = 0.20
)");
REQUIRE(maybe_st.has_value());
auto info = maybe_st.value();
auto value = info["mfix.stop_time"];
auto aa = std::get<parser::InputArray>(value);
CHECK(aa.size() == 1);
CHECK(aa[0] == 0.2);
}
TEST_CASE("string", "[]") {
auto maybe_st = parser::parse_inputs(R"(
mfix.particle_init_type = "Auto"
)");
REQUIRE(maybe_st.has_value());
auto info = maybe_st.value();
auto value = info["mfix.particle_init_type"];
auto ss = std::get<std::string>(value);
CHECK(ss == "Auto");
}
TEST_CASE("implicit string", "[]") {
auto maybe_st = parser::parse_inputs(R"(
fluid.solve = fluid
)");
REQUIRE(maybe_st.has_value());
auto info = maybe_st.value();
auto value = info["fluid.solve"];
auto ss = std::get<std::string>(value);
CHECK(ss == "fluid");
}
TEST_CASE("array", "[]") {
auto maybe_st = parser::parse_inputs(R"(
mfix.gravity = -9.81 0.0 0.0
)");
REQUIRE(maybe_st.has_value());
auto info = maybe_st.value();
auto value = info["mfix.gravity"];
auto aa = std::get<parser::InputArray>(value);
CHECK(aa.size() == 3);
CHECK(aa[0] == -9.81);
CHECK(aa[1] == 0.0);
CHECK(aa[2] == 0.0);
}
TEST_CASE("whole_file", "[]") {
auto maybe_st = parser::parse_inputs(R"(
#_______________________________________________________________________
# Solver settings
#amrex.fpe_trap_invalid = 1
#! Fluid solver
#!-----------------------------------------------------------//
mfix.cfl = 0.5
mfix.max_step = 100
#mfix.stop_time = 0.20
mfix.particle_init_type = "Auto"
mfix.gravity = -9.81 0.0 0.0
mfix.drag_type = "BVK2"
mac_proj.verbose = 0
nodal_proj.verbose = 1
amr.blocking_factor = 1
#_______________________________________________________________________
# Geometry / grids / tiles
# Maximum level in hierarchy (for now must be 0, i.e., one level in total)
amr.max_level = 0
geometry.coord_sys = 0 # 0: Cartesian
geometry.is_periodic = 0 0 0 # Is periodic in each direction?
geometry.prob_lo = 0. 0. 0. # lo corner of physical domain
geometry.prob_hi = 0.004 0.001 0.001 # hi corner of physical domain
# Number of grid cells in each direction at the coarsest level
amr.n_cell = 20 5 5
#! Grids
#!-----------------------------------------------------------//
# Maximum allowable size of each fluid subdomain in the problem domain;
# Fluid
amr.max_grid_size_x = 1024
amr.max_grid_size_y = 1024
amr.max_grid_size_z = 1024
# Particles (not with KDTree)
#particles.max_grid_size_x = 32
#particles.max_grid_size_y = 32
#particles.max_grid_size_z = 32
#! Tiles
#!-----------------------------------------------------------//
# Fluid: Maximum tile size within each grid
fabarray.mfiter_tile_size = 1024 1024 1024
# Particles: Maximum particle title size
particles.tile_size = 1024 1024 1024
#! EB settings
#!-----------------------------------------------------------//
# Level-set refinement and padding
mfix.levelset__refinement = 4 # levelset resolution
mfix.write_eb_surface = true # Needed to output eb-surfaces
#_______________________________________________________________________
# Particle load balancing
#mfix.load_balance_type = "KnapSack"
amr.dual_grid = 0
amr.regrid_int = -1
#! KnapSack settings
#!-----------------------------------------------------------//
#default is "RunTimeCosts"; options include RunTimeCosts or NumParticles
#this option is only relevant if load_balance_type = KnapSack
#mfix.knapsack_weight_type = "NumParticles"
#mfix.knapsack_weight_type = "RunTimeCosts"
#_______________________________________________________________________
# IO / Checkpointing
amr.par_ascii_int = -1
amr.par_ascii_file ="vis"
amr.plot_int = -1
amr.plot_file ="plt"
amr.check_int = -1
amr.check_file ="chk"
#! Restart from checkpoint
#!-----------------------------------------------------------//
#amr.restart ="chk00100"
#_______________________________________________________________________
# Fluid model settings
#
fluid.solve = fluid
fluid.density = constant
fluid.density.constant = 1.0
fluid.viscosity = constant
fluid.viscosity.constant = 2.0e-5
#_______________________________________________________________________
# DEM model settings
#
dem.solve = solid0
dem.friction_coeff.pp = 0.0
dem.friction_coeff.pw = 0.0
dem.spring_const.pp = 10.0
dem.spring_const.pw = 10.0
dem.restitution_coeff.solid0.solid0 = 0.8
dem.restitution_coeff.solid0.wall = 1.0
dem.spring_tang_fac.pp = 0.285714285
dem.spring_tang_fac.pw = 0.285714285
dem.damping_tang_fac.pp = 0.5
dem.damping_tang_fac.pw = 0.5
#_______________________________________________________________________
# EB geometry
#
mfix.geometry = "cylinder"
cylinder.internal_flow = true
cylinder.radius = 0.00045
cylinder.height = -1.0
cylinder.direction = 0
cylinder.center = 0.0020 0.0005 0.0005
#_______________________________________________________________________
# Regions for defining ICs and BCs
#
mfix.regions = full-domain bed inflow outflow
regions.full-domain.lo = 0.0000 0.0000 0.0000
regions.full-domain.hi = 0.0040 0.0010 0.0010
regions.bed.lo = 0.0003 0.0000 0.0000
regions.bed.hi = 0.0032 0.0010 0.0010
regions.inflow.lo = 0.0000 0.0000 0.0000
regions.inflow.hi = 0.0000 0.0010 0.0010
regions.outflow.lo = 0.0040 0.0000 0.0000
regions.outflow.hi = 0.0040 0.0010 0.0010
#_______________________________________________________________________
# Initial Conditions
#
ic.regions = full-domain bed
ic.full-domain.fluid.volfrac = 1.0
ic.full-domain.fluid.velocity = 0.015 0.0 0.0
ic.bed.fluid.volfrac = 0.725
ic.bed.fluid.velocity = 0.00 0.00 0.00
ic.bed.solids = solid0
ic.bed.packing = random
ic.bed.solid0.volfrac = 0.275
ic.bed.solid0.velocity = 0.00 0.00 0.00
ic.bed.solid0.diameter = constant
ic.bed.solid0.density = constant
ic.bed.solid0.diameter.constant = 100.0e-6
ic.bed.solid0.density.constant = 1000.0
#_______________________________________________________________________
# Boundary Conditions
#
bc.regions = inflow outflow
bc.inflow = mi
bc.inflow.fluid.volfrac = 1.0
bc.inflow.fluid.velocity = 0.015 0.0 0.0
bc.outflow = po
bc.outflow.fluid.pressure = 0.0
)");
CHECK(maybe_st.has_value());
}
#define CATCH_CONFIG_MAIN // Catch main() - only add to one cpp file
#include "catch2/catch.hpp"
Subproject commit 62609621088df6723e48ea1d164108303511755b
Subproject commit 054e44694f6c8cd24259b70f48cc09d84a9af395
Subproject commit e4aaa45a48378bf8f220a359994a42066692f194