diff --git a/SConstruct b/SConstruct index ee02edb86..d19054365 100644 --- a/SConstruct +++ b/SConstruct @@ -248,6 +248,7 @@ defaults.noDebugLinkFlags = '' defaults.warningFlags = '-Wall' defaults.buildPch = False env['pch_flags'] = [] +env['openmp_flag'] = '-fopenmp' # used to generate sample build scripts if 'gcc' in env.subst('$CC'): defaults.optimizeCcFlags += ' -Wno-inline' @@ -270,11 +271,13 @@ elif env['CC'] == 'cl': # Visual Studio defaults.warningFlags = '/W3' defaults.buildPch = True env['pch_flags'] = ['/FIpch/system.h'] + env['openmp_flag'] = '/openmp' elif 'icc' in env.subst('$CC'): defaults.cxxFlags = '-std=c++0x' defaults.ccFlags = '-vec-report0 -diag-disable 1478' defaults.warningFlags = '-Wcheck' + env['openmp_flag'] = '-openmp' elif 'clang' in env.subst('$CC'): defaults.ccFlags = '-fcolor-diagnostics' diff --git a/samples/cxx/CMakeLists.txt.in b/samples/cxx/CMakeLists.txt.in index 353f591b2..6b9263ef5 100644 --- a/samples/cxx/CMakeLists.txt.in +++ b/samples/cxx/CMakeLists.txt.in @@ -5,7 +5,7 @@ set(CMAKE_VERBOSE_MAKEFILE ON) set(CMAKE_CXX_STANDARD 11) find_package(Threads REQUIRED) - +@cmake_extra@ include_directories(@cmake_cantera_incdirs@) link_directories(@cmake_cantera_libdirs@) diff --git a/samples/cxx/SConscript b/samples/cxx/SConscript index 505d7a47d..63b0ffcff 100644 --- a/samples/cxx/SConscript +++ b/samples/cxx/SConscript @@ -4,16 +4,27 @@ Import('env', 'build', 'install', 'buildSample') # (subdir, program name, [source extensions]) samples = [ - ('combustor', 'combustor', ['cpp']), - ('flamespeed', 'flamespeed', ['cpp']), - ('kinetics1', 'kinetics1', ['cpp']), - ('NASA_coeffs', 'NASA_coeffs', ['cpp']), - ('rankine', 'rankine', ['cpp']), - ('LiC6_electrode', 'LiC6_electrode', ['cpp']) + ('combustor', 'combustor', ['cpp'], False), + ('flamespeed', 'flamespeed', ['cpp'], False), + ('kinetics1', 'kinetics1', ['cpp'], False), + ('NASA_coeffs', 'NASA_coeffs', ['cpp'], False), + ('rankine', 'rankine', ['cpp'], False), + ('LiC6_electrode', 'LiC6_electrode', ['cpp'], False), + ('openmp_ignition', 'openmp_ignition', ['cpp'], True) ] -for subdir, name, extensions in samples: +for subdir, name, extensions, openmp in samples: localenv = env.Clone() + if openmp: + localenv.Append(CXXFLAGS=env['openmp_flag'], LINKFLAGS=env['openmp_flag']) + localenv['cmake_extra'] = """ +find_package(OpenMP REQUIRED) +set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}) +set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}) +""" + else: + localenv['cmake_extra'] = '' + buildSample(localenv.Program, pjoin(subdir, name), mglob(localenv, subdir, *extensions), CPPPATH=['#include'], diff --git a/samples/cxx/openmp_ignition/openmp_ignition.cpp b/samples/cxx/openmp_ignition/openmp_ignition.cpp new file mode 100644 index 000000000..557fb6de2 --- /dev/null +++ b/samples/cxx/openmp_ignition/openmp_ignition.cpp @@ -0,0 +1,97 @@ +// Ignition delay calculation with OpenMP. This example shows how to use OpenMP +// to run multiple reactor network calculations in parallel by using separate +// Cantera objects for each thread. + +#include "cantera/zerodim.h" +#include "cantera/IdealGasMix.h" + +#include + +using namespace Cantera; + +void run() +{ + // The number of threads can be set by setting the OMP_NUM_THREADS + // environment variable before running the code. + int nThreads = omp_get_max_threads(); + writelog("Running on {} threads\n\n", nThreads); + + // Containers for Cantera objects to be used in different. Each thread needs + // to have its own set of linked Cantera objects. Multiple threads accessing + // the same objects at the same time will cause errors. + std::vector> gases; + std::vector> reactors; + std::vector> nets; + + // Create and link the Cantera objects for each thread. This step should be + // done in serial + for (int i = 0; i < nThreads; i++) { + gases.emplace_back(new IdealGasMix("gri30.xml", "gri30")); + reactors.emplace_back(new IdealGasConstPressureReactor()); + nets.emplace_back(new ReactorNet()); + reactors.back()->insert(*gases.back()); + nets.back()->addReactor(*reactors.back()); + } + + // Points at which to compute ignition delay time + size_t nPoints = 50; + vector_fp T0(nPoints); + vector_fp ignition_time(nPoints); + for (size_t i = 0; i < nPoints; i++) { + T0[i] = 1000 + 500 * ((float) i) / ((float) nPoints); + } + + // Calculate the ignition delay at each initial temperature using multiple + // threads. + // + // Note on 'schedule(static, 1)': + // This option causes points [0, nThreads, 2*nThreads, ...] to be handled by + // the same thread, rather than the default behavior of one thread handling + // points [0 ... nPoints/nThreads]. This helps balance the workload for each + // thread in cases where the workload is biased, e.g. calculations for low + // T0 take longer than calculations for high T0. + #pragma omp parallel for schedule(static, 1) + for (size_t i = 0; i < nPoints; i++) { + // Get the Cantera objects that were initialized for this thread + size_t j = omp_get_thread_num(); + IdealGasMix& gas = *gases[j]; + Reactor& reactor = *reactors[j]; + ReactorNet& net = *nets[j]; + + // Set up the problem + gas.setState_TPX(T0[i], OneAtm, "CH4:0.5, O2:1.0, N2:3.76"); + reactor.syncState(); + net.setInitialTime(0.0); + + // Integrate until we satisfy a crude estimate of the ignition delay + // time: time for T to increase by 500 K + while (reactor.temperature() < T0[i] + 500) { + net.step(); + } + + // Save the ignition delay time for this temperature + ignition_time[i] = net.time(); + } + + // Print the computed ignition delays + writelog(" T (K) t_ig (s)\n"); + writelog("-------- ----------\n"); + for (size_t i = 0; i < nPoints; i++) { + writelog("{: 8.1f} {: 10.3e}\n", T0[i], ignition_time[i]); + } +} + +int main() +{ + try { + run(); + appdelete(); + return 0; + } catch (CanteraError& err) { + // handle exceptions thrown by Cantera + std::cout << err.what() << std::endl; + std::cout << " terminating... " << std::endl; + appdelete(); + return 1; + } +}