diff --git a/Cantera/cxx/SConscript b/Cantera/cxx/SConscript index e25284322..66f9e0ca8 100644 --- a/Cantera/cxx/SConscript +++ b/Cantera/cxx/SConscript @@ -1,11 +1,36 @@ from buildutils import * -Import('env', 'buildTargets', 'installTargets') +Import('env', 'buildTargets', 'installTargets', 'demoTargets') localenv = env.Clone() + +### C++ Interface Library ### + lib = localenv.Library(pjoin('../../lib', 'ctcxx'), source=mglob(localenv, 'src', 'cpp')) inst = localenv.Install('$ct_libdir', lib) buildTargets.extend(lib) installTargets.extend(inst) + + +### Demos ### + +# (subdir, program name, [source extensions]) +demos = [('combustor', 'combustor', ['cpp']), + ('flamespeed', 'flamespeed', ['cpp']), + ('kinetics1', 'kinetics1', ['cpp']), + ('NASA_coeffs', 'NASA_coeffs', ['cpp']), + ('rankine', 'rankine', ['cpp'])] + +for subdir, name, extensions in demos: + prog = localenv.Program(pjoin('demos',subdir, name), + mglob(localenv, pjoin('demos',subdir), *extensions), + LIBS=env['cantera_libs']) + demoTargets.extend(prog) + + inst = localenv.Install(pjoin('$ct_demodir', 'cxx', subdir), + mglob(localenv, pjoin('demos', subdir), + 'csv','txt','cpp','h','^runtest')) + + installTargets.extend(inst) diff --git a/Cantera/fortran/SConscript b/Cantera/fortran/SConscript index 628416584..5437cc6bf 100644 --- a/Cantera/fortran/SConscript +++ b/Cantera/fortran/SConscript @@ -1,6 +1,6 @@ from buildutils import * -Import('env', 'buildTargets', 'installTargets') +Import('env', 'buildTargets', 'installTargets', 'demoTargets') localenv = env.Clone() @@ -25,3 +25,21 @@ for mod in mods: action=Copy('$TARGET', '$SOURCE')) localenv.AddPostAction(lib, copy) buildTargets.extend(copy) + +# (subdir, program name, [source extensions]) +demos = [('f77demos', 'ctlib', ['^ctlib.f']), + ('f77demos', 'isentropic', ['^isentropic.f'])] + +ftn_demo = localenv.Object(pjoin('f77demos','demo_ftnlib.cpp')) +for subdir, name, extensions in demos: + prog = localenv.Program(pjoin(subdir, name), + mglob(localenv, subdir, *extensions) + ftn_demo, + LIBS=env['cantera_libs']+['fct']+['stdc++'], + LINK='$F77') + demoTargets.extend(prog) + +inst = localenv.Install(pjoin('$ct_demodir', 'f77'), + mglob(localenv, 'f77demos', + 'csv','txt','cpp','f','^runtest')) + +installTargets.extend(inst) diff --git a/Cantera/fortran/f77demos/demo_ftnlib.cpp b/Cantera/fortran/f77demos/demo_ftnlib.cpp new file mode 100644 index 000000000..bd9535296 --- /dev/null +++ b/Cantera/fortran/f77demos/demo_ftnlib.cpp @@ -0,0 +1,415 @@ +/*! + A simple Fortran 77 interface + + This file is an example of how to write an interface to use Cantera + in Fortran 77 programs. The basic idea is to store pointers to + Cantera objects in global storage, and then create Fortran-callable + functions that access the objects through the pointers. + + This particular example defines functions that return thermodynamic + properties, transport properties, and kinetic rates for reacting + ideal gas mixtures. Only a single pointer to an IdealGasMix object is + stored, so only one reaction mechanism may be used at any one time in + the application. Of course, it is a simple modification to store + multiple objects if it is desired to use multiple reaction + mechanisms. + + The functions defined here are ones commonly needed in application + programs that simulate gas-phase combustion or similar + processes. Similar libraries to access other capabilities of Cantera + (surface chemistry, etc.) could be written in the same way. + + This library is designed for Fortran compilers that expect external + procedure na,es to be lowercase with a trailing underscore. If this + is not the case, the procedure names must be edited before use. + + */ + +// turn off warnings under Windows +#ifdef WIN32 +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif + +// add any other Cantera header files you need here +#include +#include + +using namespace Cantera; +using namespace Cantera_CXX; + +// store a pointer to an IdealGasMix object +static IdealGasMix* _gas = 0; + +// provides access to the pointers for functions in other libraries +IdealGasMix* _gasptr() +{ + return _gas; +} + +// comment these out to produce a smaller executable if not needed +#define WITH_EQUIL +#define WITH_TRANSPORT + +#ifdef WITH_EQUIL +#include +#endif + + +#ifdef WITH_TRANSPORT +#include + +// store a pointer to a transport manager +static Transport* _trans = 0; +Transport* _transptr() +{ + return _trans; +} +#endif + +// error handler +void handleError() +{ + showErrors(cout); + exit(-1); +} + +// extern "C" turns off C++ name-mangling, so that the procedure names +// in the object file are exactly as shown here. + +extern "C" { + + /// This is the Fortran main program. This works for g77; it may + /// need to be modified for other Fortran compilers +#ifdef NEED_ALT_MAIN + extern int MAIN__(); +#endif + + /** + * Read in a reaction mechanism file and create an IdealGasMix + * object. The file may be in Cantera input format or in CTML. (If + * you have a file in Chemkin-compatible format, use utility + * program ck2cti first to convert it into Cantera format.) + */ + void newidealgasmix_(char* file, char* id, char* transport, + ftnlen lenfile, ftnlen lenid, ftnlen lentr) + { + string trmodel = ""; + try { + string fin = string(file, lenfile); + string fth = string(id, lenid); + trmodel = string(transport, lentr); + if (_gas) delete _gas; + _gas = new IdealGasMix(fin, fth); + } catch (CanteraError) { + handleError(); + } +#ifdef WITH_TRANSPORT + try { + if (_trans) delete _trans; + _trans = newTransportMgr(trmodel,_gas,1); + } catch (CanteraError) { + _trans = newTransportMgr("",_gas,1); + } +#endif + } + + /// integer function nElements() + integer nelements_() + { + return _gas->nElements(); + } + + /// integer function nSpecies() + integer nspecies_() + { + return _gas->nSpecies(); + } + + /// integer function nReactions() + integer nreactions_() + { + return _gas->nReactions(); + } + + void getspeciesname_(integer* k, char* name, ftnlen n) + { + int ik = *k - 1; + fill(name, name + n, ' '); + string spnm = _gas->speciesName(ik); + int ns = spnm.size(); + unsigned int nmx = (ns > n ? n : ns); + copy(spnm.begin(), spnm.begin()+nmx, name); + } + + //-------------- setting the state ---------------------------- + + /// subroutine setState_TPX(T, P, X) + void setstate_tpx_(doublereal* T, doublereal* P, doublereal* X) + { + try { + _gas->setState_TPX(*T, *P, X); + } catch (CanteraError) { + handleError(); + } + } + + /// subroutine setState_TPX_String(T, P, X) + void setstate_tpx_string_(doublereal* T, doublereal* P, + char* X, ftnlen lenx) + { + try { + _gas->setState_TPX(*T, *P, string(X, lenx)); + } catch (CanteraError) { + handleError(); + } + } + + void setstate_try_(doublereal* T, doublereal* rho, doublereal* Y) + { + try { + _gas->setState_TRY(*T, *rho, Y); + } catch (CanteraError) { + handleError(); + } + } + + void setstate_tpy_(doublereal* T, doublereal* p, doublereal* Y) + { + try { + _gas->setState_TPY(*T, *p, Y); + } catch (CanteraError) { + handleError(); + } + } + + void setstate_sp_(doublereal* s, doublereal* p) + { + try { + _gas->setState_SP(*s, *p); + } catch (CanteraError) { + handleError(); + } + } + + //-------------- thermodynamic properties ---------------------- + + /// Temperature (K) + doublereal temperature_() + { + return _gas->temperature(); + } + + /// Pressure (Pa) + doublereal pressure_() + { + return _gas->pressure(); + } + + /// Density (kg/m^3) + doublereal density_() + { + return _gas->density(); + } + + /// Mean molar mass (kg/kmol). + doublereal meanmolarmass_() + { + return _gas->meanMolecularWeight(); + } + + /// Molar enthalpy (J/kmol) + doublereal enthalpy_mole_() + { + return _gas->enthalpy_mole(); + } + + /// Molar internal energy (J/kmol) + doublereal intenergy_mole_() + { + return _gas->intEnergy_mole(); + } + + /// Molar entropy (J/kmol-K) + doublereal entropy_mole_() + { + return _gas->entropy_mole(); + } + + /// Molar heat capacity at constant P (J/kmol-K) + doublereal cp_mole_() + { + return _gas->cp_mole(); + } + + /// Molar Gibbs function (J/kmol) + doublereal gibbs_mole_() + { + return _gas->gibbs_mole(); + } + + doublereal enthalpy_mass_() + { + return _gas->enthalpy_mass(); + } + + doublereal intenergy_mass_() + { + return _gas->intEnergy_mass(); + } + + doublereal entropy_mass_() + { + return _gas->entropy_mass(); + } + + doublereal cp_mass_() + { + return _gas->cp_mass(); + } + + doublereal cv_mass_() + { + return _gas->cv_mass(); + } + + doublereal gibbs_mass_() + { + return _gas->gibbs_mass(); + } + + void gotmolefractions_(doublereal* x) + { + _gas->getMoleFractions(x); + } + + void gotmassfractions_(doublereal* y) + { + _gas->getMassFractions(y); + } + +#ifdef WITH_EQUIL + void equilibrate_(char* opt, ftnlen lenopt) + { + try { + if (lenopt != 2) { + throw CanteraError("equilibrate", + "two-character string required."); + } + string optstr = string(opt, 2); + equilibrate(*_gas, optstr.c_str()); + } catch (CanteraError) { + handleError(); + } + } +#endif + + //---------------- kinetics ------------------------- + + void getreactioneqn_(integer* i, char* eqn, ftnlen n) + { + int irxn = *i - 1; + fill(eqn, eqn + n, ' '); + string e = _gas->reactionString(irxn); + int ns = e.size(); + unsigned int nmx = (ns > n ? n : ns); + copy(e.begin(), e.begin()+nmx, eqn); + } + + void getnetproductionrates_(doublereal* wdot) + { + _gas->getNetProductionRates(wdot); + } + + void getcreationrates_(doublereal* cdot) + { + _gas->getCreationRates(cdot); + } + + void getdestructionrates_(doublereal* ddot) + { + _gas->getDestructionRates(ddot); + } + + void getnetratesofprogress_(doublereal* q) + { + _gas->getNetRatesOfProgress(q); + } + + void getfwdratesofprogress_(doublereal* q) + { + _gas->getFwdRatesOfProgress(q); + } + + void getrevratesofprogress_(doublereal* q) + { + _gas->getRevRatesOfProgress(q); + } + + //-------------------- transport properties -------------------- + +#ifdef WITH_TRANSPORT + double viscosity_() + { + try { + return _trans->viscosity(); + } catch (CanteraError) { + handleError(); + return 0.0; + } + } + + double thermalconductivity_() + { + try { + return _trans->thermalConductivity(); + } catch (CanteraError) { + handleError(); + return 0.0; + } + } + + void getmixdiffcoeffs_(double* diff) + { + try { + _trans->getMixDiffCoeffs(diff); + } catch (CanteraError) { + handleError(); + } + } + + void getthermaldiffcoeffs_(double* dt) + { + try { + _trans->getThermalDiffCoeffs(dt); + } catch (CanteraError) { + handleError(); + } + } +#endif + +} + +/* + * HKM 7/22/09: + * I'm skeptical that you need this for any system. + * Definately creates an error (dupl main()) for the solaris + * system + */ +#ifdef NEED_ALT_MAIN +/** + * This C++ main program simply calls the Fortran main program. + */ +int main() +{ + try { + return MAIN__(); + } catch (CanteraError) { + showErrors(cerr); + exit(-1); + } catch (...) { + cout << "An exception was trapped. Program terminating." << endl; + exit(-1); + } +} +#endif diff --git a/Cantera/matlab/SConscript b/Cantera/matlab/SConscript index 2a6bf49a0..b4183c4a0 100644 --- a/Cantera/matlab/SConscript +++ b/Cantera/matlab/SConscript @@ -27,23 +27,9 @@ exit localenv = env.Clone() -linkLibs = ['clib','oneD','zeroD','equil','kinetics','transport', - 'thermo','ctnumerics','ctmath','tpx', - 'ctspectra','converters','ctbase'] - -if env['use_sundials']: - linkLibs.extend(('sundials_cvodes','sundials_nvecserial')) - -linkLibs.extend(localenv['blas_lapack_libs']) - -if env['build_with_f2c']: - linkLibs.append('ctf2c') -else: - linkLibs.append('gfortran') - localenv.Command('cantera/build_cantera.m', mglob(localenv, 'cantera/private', 'cpp'), - MatlabBuilder(linkLibs)) + MatlabBuilder(localenv['cantera_libs'])) localenv['ENV']['PATH'] = os.environ['PATH'] diff --git a/Cantera/python/SConscript b/Cantera/python/SConscript index 3e85fc583..cb45d5b9a 100644 --- a/Cantera/python/SConscript +++ b/Cantera/python/SConscript @@ -20,22 +20,8 @@ localenv.Append(CPPPATH=[gcv('INCLUDEPY')], SHLINKFLAGS=gcv('LDFLAGS'), CPPFLAGS=[gcv('BASECFLAGS'), gcv('OPT')]) -linkLibs = ['clib','oneD','zeroD','equil','kinetics','transport', - 'thermo','ctnumerics','ctmath','tpx', - 'ctspectra','converters','ctbase'] - -if env['use_sundials']: - linkLibs.extend(('sundials_cvodes','sundials_nvecserial')) - -linkLibs.extend(localenv['blas_lapack_libs']) - -if env['build_with_f2c']: - linkLibs.append('ctf2c') -else: - linkLibs.append('gfortran') - pymodule = localenv.SharedLibrary('Cantera/_cantera', ['src/pycantera.cpp'], - LIBS=linkLibs, + LIBS=localenv['cantera_libs'], SHLIBPREFIX='', SHLIBSUFFIX=gcv('SO')) buildTargets.extend(pymodule) @@ -43,6 +29,7 @@ buildTargets.extend(pymodule) localenv.AddPreAction(pymodule, make_ctconf) localenv.AddPostAction(pymodule, 'cd Cantera/python; $python_cmd setup.py build') +# Install the Python module if env['cantera_python_home'] is None: # Install Python module in the default location extra = '' @@ -56,3 +43,14 @@ else: inst = localenv.Command('dummy', pymodule, 'cd Cantera/python; $python_cmd setup.py install %s' % extra) installTargets.extend(inst) + +# Copy tutorials +inst = localenv.Install('$ct_tutdir', mglob(localenv, 'tutorial', 'py')) +installTargets.extend(inst) + +# Copy examples +exampleFiles = sum([localenv.Glob(pjoin('examples', '*', '*', name)) + for name in ['runtest', 'cleanup', '*.txt', '*.py', '*.csv']], []) +for f in exampleFiles: + subdir1, subdir2 = psplit(f.path)[3:5] + installTargets.extend(localenv.Install(pjoin('$ct_demodir','python',subdir1,subdir2), f)) diff --git a/SConstruct b/SConstruct index d0864f7c2..d1b8d40fe 100644 --- a/SConstruct +++ b/SConstruct @@ -153,6 +153,8 @@ if env['F90'] == 'gfortran': env['FORTRANMODDIRPREFIX'] = '-J' elif env['F90'] == 'g95': env['FORTRANMODDIRPREFIX'] = '-fmod=' +elif env['F90'] == 'ifort': + env['FORTRANMODDIRPREFIX'] = '-module ' env['FORTRANMODDIR'] = '${TARGET.dir}' @@ -250,7 +252,7 @@ env['ct_templdir'] = pjoin(env['prefix'], 'templates') env['ct_tutdir'] = pjoin(env['prefix'], 'tutorials') env['ct_docdir'] = pjoin(env['prefix'], 'doc') env['ct_dir'] = env['prefix'] -env['ct_mandir'] = pjoin(env['prefix']) +env['ct_mandir'] = pjoin(env['prefix'], 'man1') # ********************* # *** Build Cantera *** @@ -259,6 +261,7 @@ env['ct_mandir'] = pjoin(env['prefix']) buildDir = 'build' buildTargets = [] installTargets = [] +demoTargets = [] env.SConsignFile() @@ -286,12 +289,29 @@ for header in mglob(env, 'Cantera/clib/src', 'h'): inst = env.Install(pjoin('$ct_incdir','clib'), header) installTargets.extend(inst) +### List of libraries needed to link to Cantera ### +linkLibs = ['clib','oneD','zeroD','equil','kinetics','transport', + 'thermo','ctnumerics','ctmath','tpx', + 'ctspectra','converters','ctbase'] + +if env['use_sundials']: + linkLibs.extend(('sundials_cvodes','sundials_nvecserial')) + +linkLibs.extend(env['blas_lapack_libs']) + +if env['build_with_f2c']: + linkLibs.append('ctf2c') +else: + linkLibs.append('gfortran') + +env['cantera_libs'] = linkLibs + configh = env.Command('build/include/cantera/config.h', 'config.h', Copy('$TARGET', '$SOURCE')) inst = env.Install('$ct_incdir', configh) installTargets.extend(inst) # Add targets from the SConscript files in the various subdirectories -Export('env', 'buildDir', 'buildTargets', 'installTargets') +Export('env', 'buildDir', 'buildTargets', 'installTargets', 'demoTargets') VariantDir('build/ext', 'ext', duplicate=0) SConscript('build/ext/SConscript') @@ -318,8 +338,13 @@ if env['matlab_toolbox'] == 'y': VariantDir('build/tools', 'tools', duplicate=0) SConscript('build/tools/SConscript') -# Meta-targets +# Data files +inst = env.Install('$ct_datadir', mglob(env, pjoin('data','inputs'), 'cti', 'xml')) +installTargets.extend(inst) + +### Meta-targets ### build_cantera = Alias('build', buildTargets) install_cantera = Alias('install', installTargets) +build_demos = Alias('demos', demoTargets) Default(build_cantera) diff --git a/buildutils.py b/buildutils.py index 5ce676881..d5debb9e2 100644 --- a/buildutils.py +++ b/buildutils.py @@ -76,8 +76,19 @@ def quoted(s): def mglob(env, subdir, *args): - """ each arg in args is assumed to be file extension """ - return sum((env.Glob('%s/*.%s' % (subdir, ext)) for ext in args), []) + """ + Each arg in args is assumed to be file extension, + unless the arg starts with a '^', in which case the remainder + of the arg is taken to be a complete pattern. + """ + matches = [] + for ext in args: + if ext.startswith('^'): + matches += env.Glob(pjoin(subdir, ext[1:])) + else: + matches += env.Glob(pjoin(subdir, '*.%s' % ext)) + return matches +# return sum((env.Glob('%s/*.%s' % (subdir, ext)) for ext in args), []) def psplit(s): diff --git a/tools/SConscript b/tools/SConscript index 3412b98f7..d3fc5d9b0 100644 --- a/tools/SConscript +++ b/tools/SConscript @@ -14,3 +14,16 @@ for name, src, libs in programs: inst = localenv.Install('$ct_bindir', prog) buildTargets.extend(prog) installTargets.extend(inst) + +# Copy application templates +installTargets.extend( + localenv.Install(pjoin('$ct_templdir','cxx'), + mglob(localenv, pjoin('templates','cxx'), '*')) + + localenv.Install(pjoin('$ct_templdir','f90'), + mglob(localenv, pjoin('templates','f90'), '*')) + + localenv.Install(pjoin('$ct_templdir','f77'), + mglob(localenv, pjoin('templates','f77'), '*'))) + +# Copy man pages +inst = localenv.Install('$ct_mandir', mglob(localenv, 'man', '*')) +installTargets.extend(inst)