├── .gitignore ├── .travis.yml ├── LICENSE ├── MANIFEST.in ├── README.md ├── cpropep_build.py ├── ipython_doc ├── BasicRocketPerformance.ipynb └── BasicUsage.ipynb ├── pypropep ├── __init__.py ├── cpropep │ ├── __init__.py │ ├── cpropep │ │ ├── Makefile │ │ ├── doc │ │ │ └── thermo.html │ │ └── src │ │ │ ├── Makefile │ │ │ ├── Makefile.win │ │ │ ├── README.txt │ │ │ ├── cpropep.c │ │ │ ├── cpropep.conf │ │ │ ├── input.pro │ │ │ ├── propellant.dat │ │ │ └── thermo.dat │ ├── libcompat │ │ ├── Makefile │ │ ├── Makefile.win │ │ ├── include │ │ │ ├── compat.h │ │ │ └── getopt.h │ │ └── src │ │ │ ├── compat.c │ │ │ └── getopt.c │ ├── libcpropep │ │ ├── Makefile │ │ ├── Makefile.win │ │ ├── include │ │ │ ├── const.h │ │ │ ├── conversion.h │ │ │ ├── derivative.h │ │ │ ├── equilibrium.h │ │ │ ├── performance.h │ │ │ ├── print.h │ │ │ ├── return.h │ │ │ └── type.h │ │ └── src │ │ │ ├── Makefile │ │ │ ├── derivative.c │ │ │ ├── equilibrium.c │ │ │ ├── performance.c │ │ │ └── print.c │ ├── libnum │ │ ├── Makefile │ │ ├── Makefile.win │ │ ├── include │ │ │ └── num.h │ │ └── src │ │ │ ├── Makefile │ │ │ ├── general.c │ │ │ ├── lu.c │ │ │ ├── newton.c │ │ │ ├── print.c │ │ │ ├── ptfix.c │ │ │ ├── rk4.c │ │ │ ├── rkf.c │ │ │ ├── sec.c │ │ │ ├── simpson.c │ │ │ ├── spline.c │ │ │ ├── sysnewton.c │ │ │ ├── test │ │ │ ├── test.c │ │ │ └── trapeze.c │ └── libthermo │ │ ├── Makefile │ │ ├── Makefile.win │ │ ├── include │ │ ├── load.h │ │ └── thermo.h │ │ └── src │ │ ├── Makefile │ │ ├── load.c │ │ └── thermo.c ├── data │ ├── propellant.dat │ ├── references.txt │ └── thermo.dat ├── equilibrium.py ├── error.py ├── performance.py └── propellant.py ├── reference └── RP-1311.pdf ├── setup.py ├── tests ├── test_equilibrium.py ├── test_performance.py ├── test_propellant.py └── test_pypropep.py └── tox.ini /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | *.o 9 | 10 | # Distribution / packaging 11 | .Python 12 | env/ 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *,cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # IPython Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # dotenv 80 | .env 81 | 82 | # virtualenv 83 | venv/ 84 | ENV/ 85 | 86 | # Spyder project settings 87 | .spyderproject 88 | 89 | # Rope project settings 90 | .ropeproject 91 | 92 | # MacOS DS_Store 93 | ._DS_Store 94 | .DS_Store 95 | 96 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: python 3 | python: 4 | - '2.7' 5 | - '3.5' 6 | install: 7 | - pip install tox-travis 8 | script: 9 | - tox 10 | deploy: 11 | provider: pypi 12 | user: jonnyd 13 | password: 14 | secure: LcYPn+PsgvNZR5BgID5DUQNpoSFQAGDFGI2T4NyKk64xwXQw534rU+McsRc6tGdhy5cLsFH2pZR63gnaWk5yidMqttYYK5MeItZhBYxtoY8rB9cJp0BQtjOB9xXoHQVTwwzbe2lLl5EJuhQvTH6N2Qa/0RMiP98hATLko/0LA0JcQ91Wochox/0Eoa/KJ931HzcHm3zIEFYoxvARq4d1ZL/VMM8o1ErecNRegre2FmOzXR/bS2/ewBLdJujH2+5KlUeLVBPW8X4qCgfJgog3nMKjjfn4TWsQLa64PZ5TLCSItY6NtJ8l/F5gIenOoovHp/LsTGF4jeKEoKt5Zv9jmxXC2mQ0FJVr5tpWBIZH/gh2g63/nHlaLU7ehUXUJiOard7tb/SpJd7ZiV5HK2Y/YBTVhQ6MdUsJ2tIjIP7Hl49WvH2oPNoSP5pEcURGO6uSEWw4GH2OO0yNVGpeOphbZHLkeBux5InuuNF06u8RInlWhwZQQpH2R5ANW5WIXce9CbNPDBUKYajPFZ+ePXZTLHYKQPF15pJtVR3UbXZZnjd+uZGrDvspvsODfVQRdYzut8CuT4cIfE45mLOdjCdItnEg83XPECjaLs595nllf5OY30IPuCjIE0ZSarwJuby44au41mhaAO/4thyxc5rp3cZYzahyAHFyYwl6EoI2ak4= 15 | on: 16 | tags: true 17 | distributions: sdist bdist_wheel 18 | repo: jonnydyer/pypropep 19 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | recursive-include pypropep *.h -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pypropep 2 | [![Build Status](https://travis-ci.org/jonnydyer/pypropep.svg?branch=master)](https://travis-ci.org/jonnydyer/pypropep) 3 | [![Coverage Status](https://coveralls.io/repos/github/jonnydyer/pypropep/badge.svg?branch=master)](https://coveralls.io/github/jonnydyer/pypropep?branch=master) 4 | 5 | Python interfaces to [cpropep](https://sourceforge.net/projects/rocketworkbench/?source=navbar), a project started ~15 years ago by Antoine Lefebvre and Ray Calkins to implement the [classic Gordon and McBride](https://www.grc.nasa.gov/WWW/CEAWeb/RP-1311.pdf) chemical equillibrium code in C. 6 | 7 | The RocketWorkBench (and hence cpropep) project hasn't seen any activity in close to 15 years and yet I still found myself prefering cpropep over [CEA](https://www.grc.nasa.gov/WWW/CEAWeb/) for a number of reasons including accessibility, it's C-heritage (rather than Fortran) and the extensive propellant library distributed with all flavors of PROPEP. Calling it manually from the command line or writing and compiling new C executables for every analysis task is cumbersome to say the least. The goal of this project is to bring cpropep into the 21st century with a clean, useful Python interface. 8 | 9 | ## Version 0.1 10 | Today, this module is at v0.1. It is only tested and guaranteed to work on Python 2.7 (something I hope to fix for the next release) and requires the users machine to have a compiler available for installation (also hope to fix for 0.2). Otherwise, it is fairly functional. 11 | 12 | # Installation 13 | 14 | Currently the two methods for installing pypropep are pip and from source using setuptools. Pip is recommended. 15 | 16 | ## Pip 17 | Not much to it - 18 | 19 | pip install pypropep 20 | 21 | ## From source 22 | 23 | git clone https://github.com/jonnydyer/pypropep.git 24 | cd pypropep 25 | python setup.py install 26 | 27 | ## Conda 28 | 29 | Coming soon... 30 | 31 | # Usage 32 | 33 | ## Basic Usage 34 | Here is a brief example of how to use pypropep:: 35 | 36 | >>> import pypropep as ppp 37 | >>> ppp.init() 38 | Loaded 1921 thermo species 39 | Loaded 1030 propellants 40 | 41 | >>> o2 = ppp.PROPELLANTS['OXYGEN (GAS)'] 42 | >>> ch4 = ppp.PROPELLANTS['METHANE'] 43 | >>> sp = ppp.ShiftingPerformance() 44 | 45 | >>> OF = 2.8 46 | >>> sp.add_propellants_by_mass([(ch4, 1.0), (o2, OF)]) 47 | >>> sp.set_state(P=50., Pe=1.) # Pressure in atm 48 | 49 | >>> print sp.performance.cstar # in m/s 50 | 1892.82959658 51 | 52 | >>> print sp.performance.cf 53 | 1.57123484882 54 | 55 | >>> print sp.performance.Isp/9.8 # in seconds 56 | 303.477533166 57 | 58 | >>> print sp.performance.cstar * sp.performance.cf / 9.8 # in seconds 59 | 303.477533166 60 | 61 | ## iPython examples 62 | More detailed examples demonstrating the utility of the library are given in the form of two Jupyter notebooks (kindly rendered here by Git!) 63 | 64 | - [Basic Usage and Background](ipython_doc/BasicUsage.ipynb) 65 | - [Rocket Performance Examples](ipython_doc/BasicRocketPerformance.ipynb) 66 | 67 | # Roadmap 68 | 69 | ## v0.2 70 | There are several things I'd like to add to this module for the v0.2 release including: 71 | 72 | - Set up [Python Wheels](http://pythonwheels.com/) distribution so that installation doesn't require local compiling 73 | - Set up an [Anaconda](https://www.continuum.io/anaconda-overview) distribution for the module 74 | - Support Python versions other than 2.7 in both test and deployment 75 | 76 | ## v0.3 77 | - Add Finite Area Contraction-ratio (FAC) support to cpropep library and python interface 78 | -------------------------------------------------------------------------------- /cpropep_build.py: -------------------------------------------------------------------------------- 1 | # file "cpropep_build.py" 2 | import os 3 | from cffi import FFI 4 | from glob import glob 5 | 6 | ffibuilder = FFI() 7 | 8 | exclude_c_files = ['test.c'] 9 | 10 | cpropep_libs = ['libnum', 'libthermo', 'libcpropep', 'libcompat'] 11 | inc_dir = [('pypropep/cpropep/' + d + '/include/') for d in cpropep_libs] 12 | 13 | MAX_PRODUCT = 400 14 | MAX_ELEMENT = 15 15 | MAX_COMP = 20 16 | 17 | src_files = [] 18 | for l in cpropep_libs: 19 | prefix = 'pypropep/cpropep/' + l + '/src/' 20 | src_files += glob(prefix + '*.c') 21 | for ef in exclude_c_files: 22 | if (prefix + ef) in src_files: 23 | print('Excluding {} from source list'.format(prefix+ef)) 24 | src_files.remove(prefix + ef) 25 | 26 | inc_files = '' 27 | for i in inc_dir: 28 | files = glob(i + '/*.h') 29 | for f in files: 30 | inc_files += '#include "%s"\n' % (os.path.basename(f)) 31 | 32 | ffibuilder.set_source("pypropep.cpropep._cpropep", 33 | inc_files, 34 | sources=src_files, 35 | include_dirs=inc_dir) 36 | 37 | # TODO:Find a way to scrape #defines from headers rather than hard coding const 38 | ffibuilder.cdef(""" 39 | //**** libcpropep/type.h ****// 40 | typedef enum 41 | { 42 | GAS, 43 | CONDENSED, 44 | STATE_LAST, 45 | ... 46 | } state_t; 47 | 48 | typedef enum 49 | { 50 | TP, /* assign temperature and pressure */ 51 | HP, /* assign enthalpy and pressure */ 52 | SP, /* assign entropy and pressure */ 53 | ... 54 | } problem_t; 55 | 56 | typedef enum 57 | { 58 | SUBSONIC_AREA_RATIO, 59 | SUPERSONIC_AREA_RATIO, 60 | PRESSURE, 61 | ... 62 | } exit_condition_t; 63 | 64 | typedef struct _performance_prop 65 | { 66 | double ae_at; /* Exit aera / Throat aera */ 67 | double a_dotm; /* Exit aera / mass flow rate (m/s/atm) */ 68 | double cstar; /* Characteristic velocity */ 69 | double cf; /* Coefficient of thrust */ 70 | double Ivac; /* Specific impulse (vacuum) */ 71 | double Isp; /* Specific impulse */ 72 | ...; 73 | } performance_prop_t; 74 | 75 | typedef struct _composition 76 | { 77 | short ncomp; /* Number of different component */ 78 | short molecule[20]; /* Molecule code */ 79 | double coef[20]; /* Moles of molecule */ 80 | double density; /* Density of propellant */ 81 | ...; 82 | } composition_t; 83 | 84 | typedef struct _product 85 | { 86 | int element_listed; /* true if element have been listed */ 87 | int product_listed; /* true if product have been listed */ 88 | int isequil; /* true if equilibrium is ok */ 89 | 90 | /* coefficient matrix for the gases */ 91 | unsigned short A[15][400]; 92 | 93 | short n_element; /* n. of different element */ 94 | short element[15]; /* element list */ 95 | short n[STATE_LAST]; /* n. of species for each state */ 96 | short n_condensed; /* n. of total possible condensed */ 97 | short species[STATE_LAST][400]; /* possible species in each state */ 98 | double coef[STATE_LAST][400]; /* coef. of each molecule */ 99 | ...; 100 | } product_t; 101 | 102 | typedef struct _iteration_var 103 | { 104 | double n; /* mol/g of the mixture */ 105 | double ln_n; /* ln(n) */ 106 | double sumn; /* sum of all the nj */ 107 | double delta_ln_n; /* delta ln(n) in the iteration process */ 108 | double delta_ln_T; /* delta ln(T) in the iteration process */ 109 | double delta_ln_nj[400]; /* delta ln(nj) in the iteration process */ 110 | double ln_nj[400]; /* ln(nj) nj are the individual mol/g */ 111 | ...; 112 | } iteration_var_t; 113 | 114 | typedef struct _equilib_prop 115 | { 116 | double P; /* Pressure (atm) */ 117 | double T; /* Temperature (K) */ 118 | double H; /* Enthalpy (kJ/kg) */ 119 | double U; /* Internal energy (kJ/kg) */ 120 | double G; /* Gibbs free energy (kJ/kg) */ 121 | double S; /* Entropy (kJ/(kg)(K)) */ 122 | double M; /* Molar mass (g/mol) */ 123 | double dV_P; /* (d ln(V) / d ln(P))t */ 124 | double dV_T; /* (d ln(V) / d ln(T))p */ 125 | double Cp; /* Specific heat (kJ/(kg)(K)) */ 126 | double Cv; /* Specific heat (kJ/(kg)(K)) */ 127 | double Isex; /* Isentropic exponent (gamma) */ 128 | double Vson; /* Sound speed (m/s) */ 129 | ...; 130 | } equilib_prop_t; 131 | 132 | 133 | typedef struct _new_equilibrium 134 | { 135 | int equilibrium_ok; /* true if the equilibrium have been compute */ 136 | int properties_ok; /* true if the properties have been compute */ 137 | int performance_ok; /* true if the performance have been compute */ 138 | 139 | //temporarily 140 | double entropy; 141 | 142 | iteration_var_t itn; 143 | composition_t propellant; 144 | product_t product; 145 | equilib_prop_t properties; 146 | performance_prop_t performance; 147 | ...; 148 | } equilibrium_t; 149 | 150 | //**** libthermo/load.h ****// 151 | int load_thermo(char *filename); 152 | int load_propellant(char *filename); 153 | 154 | //**** libthermo/thermo.h *****// 155 | typedef struct _thermo 156 | { 157 | char name[19]; 158 | char comments[57]; 159 | int nint; /* number of different temperature interval */ 160 | char id[7]; /* identification code */ 161 | int elem[5]; 162 | int coef[5]; 163 | state_t state; 164 | double weight; /* molecular weight */ 165 | float heat; /* heat of formation at 298.15 K (J/mol) */ 166 | double dho; /* HO(298.15) - HO(0) */ 167 | float range[4][2]; /* temperature range */ 168 | int ncoef[4]; /* number of coefficient for Cp0/R */ 169 | int ex[4][8]; /* exponent in empirical equation */ 170 | 171 | double param[4][9]; 172 | 173 | /* for species with data at only one temperature */ 174 | /* especially condensed */ 175 | float temp; 176 | float enth; 177 | 178 | } thermo_t; 179 | 180 | typedef struct _propellant{ 181 | char name[120]; /* name of the propellant */ 182 | int elem[6]; /* element in the molecule (atomic number) max 6 */ 183 | int coef[6]; /* stochiometric coefficient of this element 184 | (0 for none) */ 185 | float heat; /* heat of formation in Joule/gram */ 186 | float density; /* density in g/cubic cm */ 187 | } propellant_t; 188 | 189 | extern propellant_t *propellant_list; 190 | extern thermo_t *thermo_list; 191 | 192 | extern const float molar_mass[]; 193 | extern const char symb[][3]; 194 | 195 | extern unsigned long num_thermo; 196 | extern unsigned long num_propellant; 197 | 198 | int thermo_search(char *str); 199 | int propellant_search(char *str); 200 | int atomic_number(char *symbole); 201 | int propellant_search_by_formula(char *str); 202 | double enthalpy_0(int sp, float T); 203 | double entropy_0(int sp, float T); 204 | double entropy(int sp, state_t st, double ln_nj_n, float T, float P); 205 | double specific_heat_0(int sp, float T); 206 | double mixture_specific_heat_0(equilibrium_t *e, double temp); 207 | int temperature_check(int sp, float T); 208 | double transition_temperature(int sp, float T); 209 | double propellant_enthalpy(equilibrium_t *e); 210 | double product_enthalpy(equilibrium_t *e); 211 | double product_entropy(equilibrium_t *e); 212 | double propellant_mass(equilibrium_t *e); 213 | int compute_density(composition_t *c); 214 | double gibbs_0(int sp, float T); 215 | double gibbs(int sp, state_t st, double nj_n_n, float T, float P); 216 | double heat_of_formation(int molecule); 217 | double propellant_molar_mass(int molecule); 218 | 219 | //**** libcpropep/equillibrium.h ****// 220 | int reset_element_list(equilibrium_t *e); 221 | int initialize_equilibrium(equilibrium_t *e); 222 | int reset_equilibrium(equilibrium_t *e); 223 | int copy_equilibrium(equilibrium_t *dest, equilibrium_t *src); 224 | int compute_thermo_properties(equilibrium_t *e); 225 | int set_state(equilibrium_t *e, double T, double P); 226 | int add_in_propellant(equilibrium_t *e, int sp, double mol); 227 | int equilibrium(equilibrium_t *equil, problem_t P); 228 | double product_molar_mass(equilibrium_t *e); 229 | 230 | //**** libcpropep/performance.h ****// 231 | int frozen_performance(equilibrium_t *e, exit_condition_t exit_type, 232 | double value); 233 | int shifting_performance(equilibrium_t *e, exit_condition_t exit_type, 234 | double value); 235 | """) 236 | 237 | if __name__ == "__main__": 238 | ffibuilder.compile(verbose=True) 239 | -------------------------------------------------------------------------------- /pypropep/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | from attrdict import AttrDict 3 | from .cpropep._cpropep import ffi, lib 4 | 5 | from pypropep.propellant import Propellant 6 | from pypropep.equilibrium import Equilibrium 7 | from pypropep.performance import RocketPerformance, FrozenPerformance, \ 8 | ShiftingPerformance 9 | 10 | __all__ = ['Propellant', 'Equilibrium', 'RocketPerformance', 11 | 'FrozenPerformance', 'ShiftingPerformance', 'init'] 12 | 13 | FILE_PATH = os.path.abspath(__file__) 14 | THERMO_FILE = os.path.dirname(FILE_PATH) + '/data/thermo.dat' 15 | PROPELLANT_FILE = os.path.dirname(FILE_PATH) + '/data/propellant.dat' 16 | 17 | def __convert_struct_field(s, fields): 18 | for field, fieldtype in fields: 19 | if fieldtype.type.kind == 'primitive': 20 | yield (field, getattr(s, field)) 21 | else: 22 | yield (field, convert_to_python(getattr(s, field))) 23 | 24 | 25 | def convert_to_python(s): 26 | ''' 27 | Given a cdata struct, returns a dict with all of the parsable 28 | members. Borrowed with mods from : 29 | http://stackoverflow.com/questions/20444546/python-cffi-convert-structure-to-dictionary 30 | ''' 31 | if isinstance(s, ffi.CData) == False: 32 | return s 33 | 34 | type = ffi.typeof(s) 35 | if type.kind == 'struct': 36 | return dict(__convert_struct_field(s, type.fields)) 37 | elif type.kind == 'array': 38 | if type.item.kind == 'primitive': 39 | if type.item.cname == 'char': 40 | return ffi.string(s).decode('utf-8') 41 | else: 42 | return [s[i] for i in range(type.length)] 43 | else: 44 | return [convert_to_python(s[i]) for i in range(type.length)] 45 | elif type.kind == 'primitive': 46 | return int(s) 47 | 48 | 49 | def init(thermo_file=None, propellant_file=None): 50 | global THERMO_FILE, PROPELLANT_FILE, SPECIES, PROPELLANTS 51 | if thermo_file is not None: 52 | THERMO_FILE = thermo_file 53 | if propellant_file is not None: 54 | PROPELLANT_FILE = propellant_file 55 | 56 | r = lib.load_thermo(THERMO_FILE.encode('utf-8')) 57 | if r > 0: 58 | print("Loaded {} thermo species".format(r)) 59 | else: 60 | print("Failed to load thermo file {}".format(THERMO_FILE)) 61 | 62 | r = lib.load_propellant(PROPELLANT_FILE.encode('utf-8')) 63 | 64 | if r > 0: 65 | print("Loaded {} propellants".format(r)) 66 | else: 67 | print("Failed to load propellant file {}".format(PROPELLANT_FILE)) 68 | 69 | SPECIES = dict() 70 | PROPELLANTS = dict() 71 | 72 | # Build species dict 73 | for i in range(lib.num_thermo): 74 | s = lib.thermo_list[i] 75 | l = len(SPECIES) 76 | name = ffi.string(s.name).decode('utf-8') 77 | while name in SPECIES: 78 | name += "'" 79 | SPECIES[name] = AttrDict(convert_to_python(s)) 80 | SPECIES[name]['id'] = i 81 | if len(SPECIES) <= l: 82 | raise RuntimeWarning("Species {}, {}:{} dropped".format(i, name, 83 | str(SPECIES[name]))) 84 | 85 | # Build propellants dict 86 | for i in range(lib.num_propellant): 87 | p = lib.propellant_list[i] 88 | name = ffi.string(p.name).decode('utf-8') 89 | l = len(PROPELLANTS) 90 | while name in PROPELLANTS: 91 | name += "'" 92 | PROPELLANTS[name] = Propellant(convert_to_python(p)) 93 | PROPELLANTS[name]['id'] = i 94 | if len(PROPELLANTS) <= l: 95 | raise RuntimeWarning("Propellant {}, {}:{} dropped".format(i, 96 | name, str(PROPELLANTS[name]))) 97 | 98 | def find_propellant(substr): 99 | return [value for key, value in list(PROPELLANTS.items()) 100 | if substr.lower() in key.lower()] 101 | -------------------------------------------------------------------------------- /pypropep/cpropep/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonnydyer/pypropep/ed94c4518689031ca035212c13d7a88c008b5860/pypropep/cpropep/__init__.py -------------------------------------------------------------------------------- /pypropep/cpropep/cpropep/Makefile: -------------------------------------------------------------------------------- 1 | 2 | CC = gcc 3 | COPT = -g -Wall -O3 #-pg -O6\ 4 | # -mpentium -ffast-math -funroll-loops -fnonnull-objects\ 5 | # -fno-exceptions -fforce-mem -fforce-addr -fcse-follow-jumps\ 6 | # -fexpensive-optimizations -march=pentium -fno-rtti #-fomit-frame-pointer 7 | 8 | DEF = -DGCC #-DTRUE_ARRAY 9 | 10 | all: 11 | make -C src all 12 | 13 | clean: 14 | make -C src clean 15 | 16 | deep-clean: clean 17 | make -C src deep-clean 18 | -------------------------------------------------------------------------------- /pypropep/cpropep/cpropep/doc/thermo.html: -------------------------------------------------------------------------------- 1 | 2 | The NASA thermo data file format was documented in: 3 | 4 | Sanford Gordon and Bonnie J. McBride, "Computer Program for Calculation of 5 | Complex Chemical Equilibrium Compositions and Applications: I. Analysis", 6 | NASA Reference Publication 1311, October 1994. 7 | 8 | Bonnie J. McBride and Sanford Gordon, "Computer Program for Calculation of 9 | Complex Chemical Equilibrium Compositions and Applications: II. Users Manual 10 | and Program Description", NASA Reference Publication 1311, June 1996. 11 | 12 | The equations below for nondimensional specific heat, enthalpy, and 13 | entropy, are given in Sanford and Bonnie (1994). Eqs. 4.6-4.8 are the 14 | "old" NASA format, and Eqs. 4.9-4.11 are the "new" NASA format as discussed 15 | in this file. 16 | 17 | Eq. 4.6: Cp0/R = a1 + a2*T + a3*T^2 + a4*T^3 + a5*T^4 18 | Eq. 4.7: H0/RT = a1 + a2/2*T + a3/3*T^2 + a4/4*T^3 + a5/5*T^4 + a6/T 19 | Eq. 4.8: S0/R = a1*ln(T) + a2*T + a3/2*T^2 + a4/3*T^3 + a5/4*T^4 + a7 20 | 21 | Eq. 4.9: Cp0/R = a1*T^-2 + a2*T^-1 + a3 + a4*T + a5*T^2 + a6*T^3 + a7*T^4 22 | Eq. 4.10: H0/RT = -a1*T^-2 + a2*T^-1*ln(T) + a3 + a4*T/2 + a5*T^2/3 + 23 | a6*T^3/4 + a7*T^4/5 + b1/T 24 | Eq. 4.11: S0/R = -a1*T^-2/2 - a2*T^-1 + a3*ln(T) + a4*T + a5*T^2/2 + 25 | a6*T^3/6 + a7*T^4/4 + b2 26 | 27 | The following information is quoted directly from McBride and Gordon (1996): 28 | 29 | "Appendix A: Format for Thermodynamic Data 30 | 31 | The library of thermodynamic data contains data for both reaction products 32 | and reactants. All reaction products and some reactants are in the 33 | nine-constant functional form discussed in section 4.2 of Gordon and 34 | McBride (1994). The format for these data is given here. Thermodynamic 35 | data are provided with the program on a separate file, thermo.inp. 36 | Sections 2.8 and 5.24 discuss the processing of the thermo.inp data and 37 | the storing of the processed data in thermo.lib for subsequent use in the 38 | CEA program. Names of species contained in thermo.inp are listed in 39 | Appendix B. 40 | 41 | The general format is given in table A1. This format is applicable for 42 | all gaseous species and for those condensed species whose data extend over 43 | a temperature range. For those condensed species with data given at only 44 | one temperature, the format is somewhat different. On record 2, instead 45 | of the last number being a heat of formation, it is an assigned enthalpy. 46 | (Note that if the temperature is 298.15 K, the heat of formation and the 47 | assigned enthalpy are equivalent.) The first number in record 2 (number 48 | of temperature intervals) is always zero. On record 3, only one number is 49 | given, the temperature of the assigned enthalpy on record 2. Two examples are 50 | given. Example A1, for chlorine gas, illustrates the general format. 51 | Example A2, for liquid acetylene, illustrates the format for a condensed 52 | species with data given at only one temperature. The general equations 53 | for dimensionless heat capacity, enthalpy, and entropy (eqs. (4.6) to (4.8) 54 | <sic> from Gordon and McBride, 1994) are repeated for convenience. 55 | 56 | Record Constants Format Column 57 | 1 Species name or formula A24 1 to 24 58 | Comments (data source) A56 25-80 59 | 2 Number of T intervals I2 2 60 | Optional identification code A6 4-9 61 | Chemical formulas, symbols, and numbers 5(A2,F6.2) 11-50 62 | Zero for gas and nonzero for condensed phases I1 52 63 | Molecular weight F13.5 53-65 64 | Heat of formation at 298.15 K, J/mol F13.5 66-80 65 | 3 Temperature range 2F10.3 2-21 66 | Number of coefficients for Cp0/R I1 23 67 | T exponents in empirical equation for Cp0/R 8F5.1 24-63 68 | {H0(298.15)-H0(0)}, J/mol F15.3 66-80 69 | 4 First five coefficients for Cp0/R 5D16.8 1-80 70 | 5 Last three coefficients for Cp0/R 3D16.8 1-48 71 | Integration constants b1 and b2 2D16.8 49-80 72 | ... Repeat 3, 4, and 5 for each interval 73 | 74 | Example A.1: 75 | 76 | CL2 Chlorine gas. TPIS 1989, v1, pt2, p88. 77 | 2 tpis89 CL 2.00 0.00 0.00 0.00 0.00 0 70.90540 0.000 78 | 200.000 1000.000 7 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 9181.110 79 | 3.46281724D+04 -5.54712949D+02 6.20759103D+00 -2.98963673D-03 3.17303416D-06 80 | -1.79363467D-09 4.26005863D-13 0.00000000D+00 1.53407075D+03 -9.43835303D+00 81 | 1000.000 6000.000 7 -2.0 -1.0 0.0 1.0 2.0 3.0 4.0 0.0 9181.110 82 | 6.09256675D+06 -1.94962688D+04 2.85453491D+01 -1.44996828D-02 4.46388943D-06 83 | -6.35852403D-10 3.32735931D-14 0.00000000D+00 1.21211722D+05 -1.69077832D+02 84 | 85 | Empirical equations for example A.1: 86 | 87 | Heat capacity: Cp0/R = a1*T^-2 + a2*T^-1 + a3 + a4*T + a5*T^2 + a6*T^3 + a7*T^4 88 | Enthalpy: H0(T)/(RT) = -a1*T^-2 + a2*T^-1*ln(T) + a3 + a4*T/2 + a5*T^2/3 + 89 | a6*T^3/4 + a7*T^4/5 + b1/T 90 | Entropy: S0(T)/R = -a1*T^-2/2 - a2*T^-1 + a3*ln(T) + a4*T + a5*T^2/2 + 91 | at*T^3/3 + a7*T^4/4 + b2 92 | 93 | Example A.2: 94 | 95 | C2H2(L),acetyle Acetylene. JANAF Prop.Ser.E,1/67. TRC a-3000,10/86. 96 | 0 1 3/95 C 2.00H 2.00 0.00 0.00 0.00 1 26.03788 207599.000 97 | 192.35" 98 | 99 | Notes: 100 | 1. Besides a very different file layout, the most significant change between 101 | the older (1971) NASA thermo data and the 1996 data is the generalization 102 | to any number of temperature intervals. 103 | 2. The preceding discussion only mentions the format of individual species 104 | data blocks. In addition, the thermo input file included with the NASA 105 | CEA program contains: 106 | a. Comments at the top of the file marked by exclamation (!) points in the 107 | first column 108 | b. Two lines at the beginning of the species data: 109 | i. One line containing only "thermo" 110 | ii. One line with 4 temperatures and a date 111 | c. A line containing only "END PRODUCTS" separating product species from 112 | reactants, and a line at the end of the file containing only 113 | "END REACTANTS". 114 | 3. There are some differences between the format actually used by CEA and 115 | the format described in McBride and Gordon (1996), and some undocumented 116 | features: 117 | a. In the CEA code, the actual read and format statements differ from the 118 | documentation by: 119 | i. The species name on the first line of a block is 15 characters long, 120 | not 24. The rest of the line is comments. 121 | ii. The heat of formation at the end of line 2 is read with f15.3, not f13.5 122 | iii. The temperature range at the beginning of line 3 is read as 2F11.3, 123 | not 2F10.3. 124 | iv. Line 5 is formatted as 2D16.8,16x,2D16.8 rather than 125 | 3D16.8,2D16.8. The 16x acknowledges that the third field is 126 | not actually used. The first two fields are the 6th and 7th 127 | polynomial coefficients, and the last two fields are the 8th and 128 | 9th (integration constants). 129 | b. Although the number of polynomial coefficients is included in the data, 130 | this number is almost always 7 (plus 2 integration constants). In the 131 | current NASA database, there are only 3 species that use less than 132 | 7 coefficients (P4O10(cr), P4O10(cr), and P4O10(L)). Apparently if 133 | less than 7 are used, they are the lowest numbered (a1, a2, a3, ...). 134 | 4. In the preceding excerpt from McBride and Gordon (1996), reference is 135 | made to eqs. (4.6) to (4.8). These should be eqs. (4.9) to (4.11). 136 | -------------------------------------------------------------------------------- /pypropep/cpropep/cpropep/src/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | COPT = -g -Wall -O3 #-pg 3 | 4 | LIB = -lcpropep -lthermo -lnum -lm 5 | ROOT = ../.. 6 | LIBDIR = -L$(ROOT)/libnum/lib \ 7 | -L$(ROOT)/libthermo/lib \ 8 | -L$(ROOT)/libcpropep/lib 9 | 10 | INCDIR = -I$(ROOT)/libnum/include/ \ 11 | -I$(ROOT)/libthermo/include/ \ 12 | -I$(ROOT)/libcpropep/include/ \ 13 | -I$(ROOT)/libcompat/include/ 14 | 15 | DEF = -DGCC -DCONF_FILE=\"/etc/rocketworkbench/cpropep.conf\" 16 | PROG = cpropep 17 | OBJS = cpropep.o 18 | 19 | all: $(PROG) 20 | 21 | .c.o: 22 | $(CC) $(DEF) $(INCDIR) $(COPT) -c $*.c -o $*.o 23 | 24 | $(PROG): $(OBJS) 25 | $(CC) $(COPT) $(OBJS) $(LIBDIR) $(LIB) -o $@ 26 | 27 | clean: 28 | rm -f *.o *~ 29 | 30 | deep-clean: clean 31 | rm -f $(PROG) 32 | -------------------------------------------------------------------------------- /pypropep/cpropep/cpropep/src/Makefile.win: -------------------------------------------------------------------------------- 1 | CC = bcc32 2 | CPP32 = cpp32 3 | LIBRARIAN = tlib 4 | LINKER = ilink32 5 | RC = brc32 6 | 7 | COPT = -3 -O2 -w-8012 -w-8004 -w-8057 -IC:\borland\bcc55\include 8 | LDOPT = -LC:\borland\bcc55\lib 9 | IDIR = -I..\..\libnum\ -I..\lib\ -I. 10 | 11 | LIB = compat.lib libnum.lib thermo.lib cpropep.lib 12 | 13 | LIBDIR = -L..\..\libnum\ -L..\lib\ 14 | 15 | DEF = -DBORLAND 16 | 17 | PROG = cpropep.exe 18 | OBJS = cpropep.obj getopt.obj 19 | 20 | .SUFFIXES: .c 21 | 22 | all: $(PROG) 23 | 24 | .c.obj: 25 | $(CC) $(COPT) $(IDIR) $(DEF) -c $*.c -o $*.obj 26 | 27 | $(PROG): $(OBJS) 28 | $(CC) $(LDOPT) $(LIBDIR) $(LIB) $(OBJS) 29 | 30 | clean: 31 | del *.obj 32 | del *.bak 33 | del *.tds 34 | 35 | deep-clean: clean 36 | del $(PROG) 37 | -------------------------------------------------------------------------------- /pypropep/cpropep/cpropep/src/README.txt: -------------------------------------------------------------------------------- 1 | This is cpropep version 1.0, a program for determining the 2 | theoretical performance of rocket propellant compositions. 3 | Cpropep is released under the GPL and is written in ANSI C. 4 | It has compiled successfully under the following platforms: 5 | 6 | . i686 Windows 98 (4.10.1998) running Microsoft Visual 7 | C++ 6.0 Standard Edition with Microsoft Visual Studio 8 | Service Pack 2 9 | . i686 Red Hat Linux 6.2 (Kernel 2.2.14-12) with gcc 10 | . i586 Debian GNU/Linux 2.2 (Kernel 2.2.16) with gcc 11 | 12 | To compile under linux, type make in the top-level directory. 13 | Under MSVC++, create a new Win32 console project file, add 14 | all source and header files to the project, set the header 15 | file directory to ..\lib\ (or copy all files from \lib to 16 | \cpropep or change the #include statements in cpropep.c) and 17 | build. 18 | 19 | 20 | Run using 21 | cpropep -h 22 | for command-line help and usage. 23 | 24 | 25 | Cpropep is Copyright (C) 2000 Antoine Lefebvre 26 | <antoinelefebvre@softhome.net> 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /pypropep/cpropep/cpropep/src/cpropep.conf: -------------------------------------------------------------------------------- 1 | thermo /usr/share/rocketworkbench/cpropep/thermo.dat 2 | propellant /usr/share/rocketworkbench/cpropep/propellant.dat 3 | -------------------------------------------------------------------------------- /pypropep/cpropep/cpropep/src/input.pro: -------------------------------------------------------------------------------- 1 | # Cpropep is based on the theory presented by Gordon and McBride 2 | # in the NASA report RP-1311. You can download a pdf version of 3 | # this document at http://www.arocket.net/library/ 4 | 5 | # The thermodynamics data file thermo.dat coma also from McBride 6 | # at the NASA Gleen Research center. 7 | 8 | # Here is an example of an input file to be use by cpropep. 9 | # Any line beginning by a '#' a space or a new_line is considered 10 | # as a comment. 11 | 12 | # This file should first contain a section named 'Propellant' which 13 | # contain a list of all substance contain in the propellant. The 14 | # number refer to an element in the data file containing propellant 15 | # information. In order to have a list of the substance, you could 16 | # invoque the program like that: 'cpropep -p' 17 | 18 | # There is two units that are support for ingredient quantity g (gram) or m (mole) 19 | 20 | #Propellant HTPB/KClO4/Al 21 | #+108 78 g 22 | #+788 11 g 23 | #+34 7 g 24 | #+788 8.4 g 25 | #+108 62 g 26 | #+493 18 g 27 | 28 | #Propellant DEXTROSE/KNO3 29 | #+1024 35 g 30 | #+765 65 g 31 | 32 | #Propellant DEXTROSE/KNO3/AL 33 | #+1024 10 g 34 | #+765 37 g 35 | #+34 20 g 36 | 37 | #Propellant PVC/AIR 38 | #+1030 60 g 39 | #+15 300 g 40 | 41 | #Propellant H2O2/OCTANE 42 | #+673 12 g 43 | #+469 80 g 44 | 45 | #Propellant O2/OCTANE 46 | #+686 51 g 47 | #+673 20 g 48 | 49 | Propellant O2/PROPANE 50 | +686 51 g 51 | +771 20 g 52 | 53 | #Propellant O2/NH3 54 | #+686 28 g 55 | #+54 20 g 56 | 57 | #Propellant NITRIC ACID/OCTANE 58 | #+630 80 g 59 | #+673 19 g 60 | 61 | # You could then specify a list of problem to be solve. There is 4 62 | # possible cases: 63 | 64 | # TP for temperature-pressure fixed problem 65 | # You have to specify the temperature and the pressure (of course) 66 | # There is 4 pressure units (psi, kPa, atm and bar) and 3 temperature units (k, c and f) 67 | 68 | #TP 69 | #+chamber_pressure 500 psi 70 | #+chamber_temperature 673 k 71 | 72 | # HP for enthalpy-pressure fixed problem. It use the enthalpy of 73 | # the propellant describe at the beginning. 74 | 75 | # Only the chamber pressure shoud be specified. The temperature of 76 | # the product will be the adiabatic flame temperature. 77 | 78 | #HP 79 | #+chamber_pressure 20.4 atm # 136 atm 80 | 81 | # FR is used to compute frozen performance. 82 | # You have to specify the chamber pressure and an exit condition. 83 | # This condition could be one of the following three: 84 | 85 | # exit_pressure: pressure at the exit. 86 | # supersonic_area_ratio: exit to throat area for an area after the nozzle 87 | # subsonic_area_ratio: exit to throat area for an area before any nozzle 88 | 89 | FR 90 | +chamber_pressure 40 atm 91 | +exit_pressure 1 atm 92 | #+supersonic_area_ratio 8.566 93 | #+subsonic_area_ratio 5 94 | 95 | # EQ is used to compute shifting equilibrium performance. 96 | # The options are the same as for frozen. 97 | 98 | EQ 99 | +chamber_pressure 40 atm 100 | +exit_pressure 1 atm 101 | #+supersonic_area_ratio 10 102 | #+subsonic_area_ratio 5 103 | -------------------------------------------------------------------------------- /pypropep/cpropep/libcompat/Makefile: -------------------------------------------------------------------------------- 1 | 2 | CC = gcc 3 | COPT = -g -Wall -O3 -pg #-O6\ 4 | # -mpentium -ffast-math -funroll-loops -fnonnull-objects\ 5 | # -fno-exceptions -fforce-mem -fforce-addr -fcse-follow-jumps\ 6 | # -fexpensive-optimizations -march=pentium -fno-rtti #-fomit-frame-pointer 7 | 8 | INCLUDEDIR = -I../../libnum/ -I. 9 | 10 | DEF = -DGCC #-DTRUE_ARRAY 11 | 12 | CPROPEP_LIBNAME = libcpropep.a 13 | THERMO_LIBNAME = libthermo.a 14 | 15 | THERMO_LIBOBJS = load.o thermo.o 16 | CPROPEP_LIBOBJS = equilibrium.o print.o performance.o derivative.o 17 | 18 | .SUFFIXES: .c 19 | 20 | all: $(CPROPEP_LIBNAME) $(THERMO_LIBNAME) 21 | 22 | .c.o: 23 | $(CC) $(DEF) $(INCLUDEDIR) $(COPT) -c $*.c -o $*.o 24 | 25 | $(CPROPEP_LIBNAME): $(CPROPEP_LIBOBJS) 26 | ar -r $@ $(CPROPEP_LIBOBJS) 27 | ranlib $@ 28 | 29 | $(THERMO_LIBNAME): $(THERMO_LIBOBJS) 30 | ar -r $@ $(THERMO_LIBOBJS) 31 | ranlib $@ 32 | 33 | clean: 34 | rm -f *.o *~ 35 | 36 | deep-clean: clean 37 | rm -f $(CPROPEP_LIBNAME) $(THERMO_LIBNAME) 38 | -------------------------------------------------------------------------------- /pypropep/cpropep/libcompat/Makefile.win: -------------------------------------------------------------------------------- 1 | 2 | CC = bcc32 3 | CPP32 = cpp32 4 | LIBRARIAN = tlib 5 | LINKER = ilink32 6 | RC = brc32 7 | 8 | COPT = -3 -O2 -w-8004 -w-8012 -w-8057 -IC:\borland\bcc55\include 9 | LDOPT = -LC:\borland\bcc55\lib 10 | 11 | INCLUDEDIR = -I..\..\libnum\ -I. 12 | 13 | DEF = -DBORLAND 14 | 15 | COMPAT_LIBNAME = compat.lib 16 | CPROPEP_LIBNAME = cpropep.lib 17 | THERMO_LIBNAME = thermo.lib 18 | 19 | COMPAT_LIBOBJS = compat.obj getopt.obj 20 | THERMO_LIBOBJS = load.obj thermo.obj 21 | CPROPEP_LIBOBJS = equilibrium.obj print.obj performance.obj derivative.obj 22 | 23 | TLIBCOMPAT = +compat.obj +getopt.obj 24 | TLIBTHERMO = +load.obj +thermo.obj 25 | TLIBCPROPEP = +equilibrium.obj +print.obj +performance.obj +derivative.obj 26 | .SUFFIXES: .c 27 | 28 | all: $(CPROPEP_LIBNAME) $(THERMO_LIBNAME) $(COMPAT_LIBNAME) 29 | 30 | .c.obj: 31 | $(CC) $(DEF) $(INCLUDEDIR) $(COPT) -c $*.c -o $*.obj 32 | 33 | $(COMPAT_LIBNAME): $(COMPAT_LIBOBJS) 34 | tlib $@ $(TLIBCOMPAT) 35 | 36 | $(CPROPEP_LIBNAME): $(CPROPEP_LIBOBJS) 37 | tlib $@ $(TLIBCPROPEP) 38 | 39 | $(THERMO_LIBNAME): $(THERMO_LIBOBJS) 40 | tlib $@ $(TLIBTHERMO) 41 | 42 | clean: 43 | del *.obj 44 | del *.bak 45 | del *.tds 46 | 47 | deep-clean: clean 48 | del $(COMPAT_LIBNAME) 49 | del $(CPROPEP_LIBNAME) 50 | del $(THERMO_LIBNAME) 51 | -------------------------------------------------------------------------------- /pypropep/cpropep/libcompat/include/compat.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPAT_H 2 | #define COMPAT_H 3 | 4 | /* 5 | Checking for _MSC_VER will detect whether MSVC++ is being used. 6 | I don't know of the other compiler flags, so others will want to 7 | add to this for their own compilers. 8 | 9 | Mark Pinese 24/4/2000 10 | */ 11 | 12 | 13 | 14 | 15 | #ifdef _MSC_VER 16 | /* MSVC++ 6.0 Std */ 17 | #define STRNCASECMP _strnicmp 18 | 19 | /* add for MSVC++ 5.0 */ 20 | #define STRCASECMP _stricmp 21 | 22 | #ifdef _DEBUG 23 | #include <crtdbg.h> 24 | #endif /* defined (_DEBUG) */ 25 | 26 | #ifndef __cplusplus 27 | typedef enum 28 | { 29 | false = 0, 30 | true = 1 31 | } bool; 32 | #endif /* !defined (__cplusplus) */ 33 | 34 | #endif /* define _MSC_VER */ 35 | 36 | #ifdef __GNUC__ 37 | 38 | #define STRCASECMP strcasecmp 39 | #define STRNCASECMP strncasecmp 40 | #define __min(a, b) ( (a) <= (b) ? (a) : (b)) 41 | #define __max(a, b) ( (a) >= (b) ? (a) : (b)) 42 | 43 | typedef enum 44 | { 45 | false = 0, 46 | true = 1 47 | } bool; 48 | 49 | #endif /* define __GNUC__ */ 50 | 51 | #ifdef BORLAND 52 | 53 | int StrNCaseCmp(const char *s1, const char *s2, size_t sz); 54 | 55 | #define STRNCASECMP StrNCaseCmp 56 | #define __min(a, b) ( (a) <= (b) ? (a) : (b)) 57 | #define __max(a, b) ( (a) >= (b) ? (a) : (b)) 58 | 59 | typedef enum 60 | { 61 | false = 0, 62 | true = 1 63 | } bool; 64 | 65 | #endif /* define BORLAND */ 66 | 67 | 68 | #endif /* !defined(COMPAT_H) */ 69 | -------------------------------------------------------------------------------- /pypropep/cpropep/libcompat/include/getopt.h: -------------------------------------------------------------------------------- 1 | /* getopt.h from Don Libes "Obfuscated C" (extended by PRW) */ 2 | #ifndef GETOPT_H 3 | #define GETOPT_H 4 | #if 0 5 | extern int getopt(int argc, char **argv, char *opts); 6 | extern char *optarg; /* current argv string */ 7 | extern int optind; /* current argv index */ 8 | extern int optopt; /* option character */ 9 | extern int opterr; /* getopt prints errors if 1 */ 10 | #endif 11 | #endif /* GETOPT_H */ 12 | 13 | -------------------------------------------------------------------------------- /pypropep/cpropep/libcompat/src/compat.c: -------------------------------------------------------------------------------- 1 | 2 | #ifdef BORLAND 3 | 4 | #include <string.h> 5 | #include <stdlib.h> 6 | #include <ctype.h> 7 | 8 | /* Compare two strings, ignoring case */ 9 | static int strcasecmp(const char *s1, const char *s2) 10 | { 11 | unsigned int i, len = strlen(s1)+1; 12 | char c1, c2; 13 | 14 | for (i = 0; i < len; ++i) 15 | { 16 | c1 = tolower(s1[i]); 17 | c2 = tolower(s2[i]); 18 | if (c1 != c2) 19 | { 20 | return ((c1 > c2) ? (1) : (-1)); 21 | } 22 | } 23 | return 0; 24 | } 25 | 26 | /* Compare two strings up to n characters, ignoring case */ 27 | static int strncasecmp(const char *s1, const char *s2, size_t sz) 28 | { 29 | unsigned int i, len = strlen(s1)+1; 30 | char c1, c2; 31 | 32 | if (sz < len) len = sz; 33 | 34 | for (i = 0; i < len; ++i) 35 | { 36 | c1 = tolower(s1[i]); 37 | c2 = tolower(s2[i]); 38 | if (c1 != c2) 39 | { 40 | return ((c1 > c2) ? (1) : (-1)); 41 | } 42 | } 43 | return 0; 44 | } 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /pypropep/cpropep/libcompat/src/getopt.c: -------------------------------------------------------------------------------- 1 | /* file getopt.c */ 2 | /* Author: Peter Wilson */ 3 | /* Catholic University and NIST */ 4 | /* pwilson@cme.nist.gov */ 5 | /* */ 6 | /* getopt() from Don Libes "Obfuscated C" */ 7 | 8 | #include <stdio.h> 9 | #include <string.h> 10 | 11 | /* getopt() -- parse command line arguments */ 12 | /* Original Author: AT&T */ 13 | /* This version from Don Libes "Obfuscated C and Other Mysteries" */ 14 | /* John Wiley & Sons, 1993. Chapter 6 */ 15 | 16 | #define ERR(s, c) if (opterr) {\ 17 | char errbuf[3];\ 18 | errbuf[0] = c; errbuf[1] = '\n'; errbuf[2] = '\0'; \ 19 | fprintf(stderr, "%s", argv[0]);\ 20 | fprintf(stderr, "%s", s);\ 21 | fprintf(stderr, "%s", errbuf); } 22 | 23 | 24 | int opterr = 1; /* getopt prints errors if this is one */ 25 | int optind = 1; /* token pointer */ 26 | int optopt; /* option character passed back to user */ 27 | char *optarg; /* flag argument (or value) */ 28 | 29 | /* return option option character, EOF if no more or ? if problem */ 30 | int getopt(int argc, char **argv, char *opts) /* opts: option string */ 31 | { 32 | static int sp = 1; /* character index in current token */ 33 | register char *cp; /* pointer into current token */ 34 | 35 | if (sp == 1) { 36 | /* check for more flag-like tokens */ 37 | if(optind >= argc || 38 | argv[optind][0] != '-' || argv[optind][1] == '\0') 39 | return(EOF); 40 | else if(strcmp(argv[optind], "--") == 0) { 41 | optind++; 42 | return(EOF); 43 | } 44 | } 45 | optopt = argv[optind][sp]; 46 | if(optopt == ':' || ( cp = strchr(opts, optopt)) == 0) { 47 | ERR(": illegal option -- ", optopt); 48 | /* if no chars left in this token, move to next token */ 49 | if(argv[optind][++sp] == '\0') { 50 | optind++; 51 | sp = 1; 52 | } 53 | return('?'); 54 | } 55 | 56 | if(*++cp == ':') {/* if a value is expected, get it */ 57 | if(argv[optind][sp+1] != '\0') 58 | /* flag value is rest of current token */ 59 | optarg = &argv[optind++][sp+1]; 60 | else if (++optind >= argc) { 61 | ERR(": option requires an argument -- ", optopt); 62 | sp = 1; 63 | return('?'); 64 | } else 65 | /* flag value is next token */ 66 | optarg = argv[optind++]; 67 | sp = 1; 68 | } else { 69 | /* set up to look at next char in token, next time */ 70 | if(argv[optind][++sp] == '\0') { 71 | /* no m ore in current token, set up next token */ 72 | sp = 1; 73 | optind++; 74 | } 75 | optarg = 0; 76 | } 77 | return(optopt); /* return current flag character found */ 78 | } 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /pypropep/cpropep/libcpropep/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: 3 | @mkdir -p lib 4 | make -C src all 5 | 6 | clean: 7 | @rm -rf lib 8 | make -C src clean 9 | 10 | deep-clean: clean 11 | make -C src deep-clean 12 | -------------------------------------------------------------------------------- /pypropep/cpropep/libcpropep/Makefile.win: -------------------------------------------------------------------------------- 1 | 2 | CC = bcc32 3 | CPP32 = cpp32 4 | LIBRARIAN = tlib 5 | LINKER = ilink32 6 | RC = brc32 7 | 8 | COPT = -3 -O2 -w-8004 -w-8012 -w-8057 -IC:\borland\bcc55\include 9 | LDOPT = -LC:\borland\bcc55\lib 10 | 11 | INCLUDEDIR = -I..\..\libnum\ -I. 12 | 13 | DEF = -DBORLAND 14 | 15 | COMPAT_LIBNAME = compat.lib 16 | CPROPEP_LIBNAME = cpropep.lib 17 | THERMO_LIBNAME = thermo.lib 18 | 19 | COMPAT_LIBOBJS = compat.obj getopt.obj 20 | THERMO_LIBOBJS = load.obj thermo.obj 21 | CPROPEP_LIBOBJS = equilibrium.obj print.obj performance.obj derivative.obj 22 | 23 | TLIBCOMPAT = +compat.obj +getopt.obj 24 | TLIBTHERMO = +load.obj +thermo.obj 25 | TLIBCPROPEP = +equilibrium.obj +print.obj +performance.obj +derivative.obj 26 | .SUFFIXES: .c 27 | 28 | all: $(CPROPEP_LIBNAME) $(THERMO_LIBNAME) $(COMPAT_LIBNAME) 29 | 30 | .c.obj: 31 | $(CC) $(DEF) $(INCLUDEDIR) $(COPT) -c $*.c -o $*.obj 32 | 33 | $(COMPAT_LIBNAME): $(COMPAT_LIBOBJS) 34 | tlib $@ $(TLIBCOMPAT) 35 | 36 | $(CPROPEP_LIBNAME): $(CPROPEP_LIBOBJS) 37 | tlib $@ $(TLIBCPROPEP) 38 | 39 | $(THERMO_LIBNAME): $(THERMO_LIBOBJS) 40 | tlib $@ $(TLIBTHERMO) 41 | 42 | clean: 43 | del *.obj 44 | del *.bak 45 | del *.tds 46 | 47 | deep-clean: clean 48 | del $(COMPAT_LIBNAME) 49 | del $(CPROPEP_LIBNAME) 50 | del $(THERMO_LIBNAME) 51 | -------------------------------------------------------------------------------- /pypropep/cpropep/libcpropep/include/const.h: -------------------------------------------------------------------------------- 1 | #ifndef const_h 2 | #define const_h 3 | 4 | /* molar gaz constant in J/(mol K)*/ 5 | #define R 8.31451 6 | 7 | /* earth gravitational acceleration */ 8 | #define Ge 9.80665 9 | 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /pypropep/cpropep/libcpropep/include/conversion.h: -------------------------------------------------------------------------------- 1 | #ifndef conversion_h 2 | #define conversion_h 3 | 4 | 5 | /* pressure units */ 6 | enum 7 | { 8 | ATM, 9 | PSI, 10 | BAR, 11 | KPA 12 | }; 13 | 14 | /* Transform calories to joules */ 15 | #define CAL_TO_JOULE 4.1868 16 | 17 | /* Transform pound/(cubic inch) to gram/(cubic centimeter) */ 18 | #define LBS_IN3_TO_G_CM3 27.679905 19 | 20 | /* Transform different pressure units */ 21 | 22 | #define ATM_TO_PA 101325.0 23 | #define ATM_TO_PSI 14.695949 24 | #define ATM_TO_BAR 1.01325 25 | 26 | #define BAR_TO_PSI 14.503774 27 | 28 | #define BAR_TO_ATM 0.98692327 29 | #define PSI_TO_ATM 0.068045964 30 | #define KPA_TO_ATM 0.0098692327 31 | 32 | /* Length */ 33 | 34 | #define M_TO_CM 100.0 35 | #define M_TO_IN 39.370079 36 | 37 | #define IN_TO_M 0.0254 38 | 39 | /* Surface */ 40 | 41 | #define M2_TO_CM2 10000.0 42 | #define M2_TO_IN2 1550.0031 43 | 44 | /* Volume */ 45 | 46 | #define M3_TO_CM3 1000000.0 47 | #define M3_TO_IN3 61023.744 48 | 49 | /* Mass flow */ 50 | 51 | #define KG_S_TO_LB_S 2.2046226 52 | 53 | 54 | /* force */ 55 | 56 | /* newton to pound-force */ 57 | #define N_TO_LBF 0.22480894 58 | #define LBF_TO_N 4.4482216 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /pypropep/cpropep/libcpropep/include/derivative.h: -------------------------------------------------------------------------------- 1 | #ifndef derivative_h 2 | #define derivative_h 3 | 4 | #include "equilibrium.h" 5 | 6 | int derivative(equilibrium_t *e); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /pypropep/cpropep/libcpropep/include/equilibrium.h: -------------------------------------------------------------------------------- 1 | #ifndef equilibrium_h 2 | #define equilibrium_h 3 | /* equilibrium.h - Calculation of Complex Chemical Equilibrium */ 4 | /* $Id: equilibrium.h,v 1.1 2000/10/13 19:24:31 antoine Exp $ */ 5 | /* Copyright (C) 2000 */ 6 | /* Antoine Lefebvre <antoine.lefebvre@polymtl.ca> */ 7 | /* Mark Pinese <pinese@cyberwizards.com.au> */ 8 | /* */ 9 | /* Licensed under the GPLv2 */ 10 | 11 | #include "compat.h" 12 | #include "type.h" 13 | 14 | #define GRAM_TO_MOL(g, sp) g/propellant_molar_mass(sp) 15 | 16 | #define _min(a, b, c) __min( __min(a, b), c) 17 | #define _max(a, b, c) __max( __max(a, b), c) 18 | 19 | extern int global_verbose; 20 | 21 | 22 | /*************************************************************** 23 | FUNCTION PROTOTYPE SECTION 24 | ****************************************************************/ 25 | 26 | int set_verbose(equilibrium_t *e, int v); 27 | 28 | /************************************************************ 29 | FUNCTION: This function search for all elements present in 30 | the composition and fill the list with the 31 | corresponding number. 32 | 33 | PARAMETER: e is of type equilibrium_t and hold the information 34 | about the propellant composition. 35 | 36 | COMMENTS: It fill the member element in equilibrium_t 37 | 38 | DATE: February 6, 2000 39 | 40 | AUTHOR: Antoine Lefebvre 41 | **************************************************************/ 42 | int list_element(equilibrium_t *e); 43 | int reset_element_list(equilibrium_t *e); 44 | 45 | int list_product(equilibrium_t *e); 46 | 47 | /*************************************************************** 48 | FUNCTION: This function initialize the equilibrium structure. 49 | The function allocate memory for all the structure 50 | it need. It is important to call dealloc_equilibrium 51 | after. 52 | 53 | AUTHOR: Antoine Lefebvre 54 | 55 | DATE: February 27, 2000 56 | ****************************************************************/ 57 | int initialize_equilibrium(equilibrium_t *e); 58 | 59 | 60 | /*************************************************************** 61 | FUNCTION: Dealloc what have been allocated by 62 | initialize_equilibrium 63 | ***************************************************************/ 64 | int dealloc_equilibrium(equilibrium_t *e); 65 | 66 | int reset_equilibrium(equilibrium_t *e); 67 | 68 | int copy_equilibrium(equilibrium_t *dest, equilibrium_t *src); 69 | 70 | int compute_thermo_properties(equilibrium_t *e); 71 | 72 | /*************************************************************** 73 | FUNCTION: Set the state at which we want to compute the 74 | equilibrium. 75 | 76 | PARAMETER: e is a pointer to an equilibrium_t structure 77 | T is the temperature in deg K 78 | P is the pressure in atm 79 | 80 | AUTHOR: Antoine Lefebvre 81 | ****************************************************************/ 82 | int set_state(equilibrium_t *e, double T, double P); 83 | 84 | 85 | /*************************************************************** 86 | FUNCTION: Add a new molecule in the propellant 87 | 88 | PARAMETER: e is a pointer to the equilibrium_t structure 89 | sp is the number of the molecule in the list 90 | mol is the quantity in mol 91 | 92 | AUTHOR: Antoine Lefebvre 93 | ****************************************************************/ 94 | int add_in_propellant(equilibrium_t *e, int sp, double mol); 95 | 96 | /*************************************************************** 97 | FUNCTION: Return the stochiometric coefficient of an element 98 | in a molecule. If the element isn't present, it return 0. 99 | 100 | COMMENTS: There is a different function for the product and for the 101 | propellant. 102 | 103 | AUTHOR: Antoine Lefebvre 104 | ****************************************************************/ 105 | int product_element_coef(int element, int molecule); 106 | //int propellant_element_coef(int element, int molecule); 107 | 108 | 109 | 110 | /*************************************************************** 111 | FUNCTION: This function fill the matrix in function of the data 112 | store in the structure equilibrium_t. The solution 113 | of this matrix give corresction to initial estimate. 114 | 115 | COMMENTS: It use the theory explain in 116 | "Computer Program for Calculation of Complex Chemical 117 | Equilibrium Compositions, Rocket Performance, Incident 118 | and Reflected Shocks, and Chapman-Jouguet Detonations" 119 | by Gordon and McBride 120 | 121 | AUTHOR: Antoine Lefebvre 122 | ****************************************************************/ 123 | 124 | 125 | //#ifdef TRUE_ARRAY 126 | //int fill_equilibrium_matrix(double *matrix, equilibrium_t *e, problem_t P); 127 | int fill_matrix(double *matrix, equilibrium_t *e, problem_t P); 128 | //#else 129 | //int fill_equilibrium_matrix(double **matrix, equilibrium_t *e, problem_t P); 130 | //int fill_matrix(double **matrix, equilibrium_t *e, problem_t P); 131 | //#endif 132 | 133 | 134 | /**************************************************************** 135 | FUNCTION: This function compute the equilibrium composition at 136 | at specific pressure/temperature point. It use fill_matrix 137 | to obtain correction to initial estimate. It correct the 138 | value until equilibrium is obtain. 139 | 140 | AUTHOR: Antoine Lefebvre 141 | ******************************************************************/ 142 | int equilibrium(equilibrium_t *equil, problem_t P); 143 | 144 | 145 | double product_molar_mass(equilibrium_t *e); 146 | 147 | #endif 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /pypropep/cpropep/libcpropep/include/performance.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef performance_h 3 | #define performance_h 4 | 5 | #include "compat.h" 6 | #include "return.h" 7 | 8 | #include "equilibrium.h" 9 | #include "derivative.h" 10 | 11 | 12 | int frozen_performance(equilibrium_t *e, exit_condition_t exit_type, 13 | double value); 14 | int shifting_performance(equilibrium_t *e, exit_condition_t exit_type, 15 | double value); 16 | 17 | #endif 18 | 19 | -------------------------------------------------------------------------------- /pypropep/cpropep/libcpropep/include/print.h: -------------------------------------------------------------------------------- 1 | #ifndef print_h 2 | #define print_h 3 | 4 | #include "type.h" 5 | 6 | #include "equilibrium.h" 7 | #include "derivative.h" 8 | #include "performance.h" 9 | 10 | #define PROPELLANT_NAME(sp) (propellant_list + sp)->name 11 | 12 | extern FILE * errorfile; 13 | extern FILE * outputfile; 14 | 15 | int print_error_message(int error_code); 16 | 17 | /*************************************************************** 18 | FUNCTION: Print the information of a specie in the thermo_list 19 | 20 | PARAMETER: an integer corresponding to the molecule 21 | 22 | AUTHOR: Antoine Lefebvre 23 | ***************************************************************/ 24 | int print_propellant_info(int sp); 25 | int print_thermo_info(int sp); 26 | 27 | 28 | /************************************************************* 29 | FUNCTION: Print the content of the respective list with the 30 | number which refer to the molecule 31 | 32 | AUTHOR: Antoine Lefebvre 33 | modification by Mark Pinese 34 | **************************************************************/ 35 | int print_thermo_list(void); 36 | int print_propellant_list(void); 37 | 38 | /************************************************************* 39 | FUNCTION: Print the list of condensed species in the product 40 | 41 | PARAMETER: a structure of tupe product_t 42 | 43 | AUTHOR: Antoine Lefebvre 44 | **************************************************************/ 45 | int print_condensed(product_t p); 46 | 47 | 48 | 49 | /************************************************************* 50 | FUNCTION: Print the list of gazeous species in the product 51 | 52 | PARAMETER: a structure of tupe product_t 53 | 54 | AUTHOR: Antoine Lefebvre 55 | **************************************************************/ 56 | int print_gazeous(product_t p); 57 | 58 | int print_product_composition(equilibrium_t *e, short npt); 59 | 60 | int print_product_properties(equilibrium_t *e, short npt); 61 | 62 | int print_propellant_composition(equilibrium_t *e); 63 | 64 | //int print_derivative_results(deriv_t *d); 65 | //int print_performance_information(performance_t *p); 66 | 67 | int print_performance_information(equilibrium_t *e, short npt); 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /pypropep/cpropep/libcpropep/include/return.h: -------------------------------------------------------------------------------- 1 | #ifndef RETURN_H 2 | #define RETURN_H 3 | 4 | /* Codes used in some functions by Antoine Lefebvre */ 5 | #define SUCCESS 0 6 | #define ERROR -1 7 | 8 | /* 9 | Return codes 10 | Mark Pinese 24/4/2000 11 | */ 12 | 13 | #define ERR_MALLOC -1 14 | #define ERR_FOPEN -2 15 | #define ERR_EOF -3 16 | #define ERR_NOT_ALLOC -4 17 | #define ERR_TOO_MUCH_PRODUCT -5 18 | #define ERR_EQUILIBRIUM -6 19 | #define ERR_AERA_RATIO -7 20 | #define ERR_RATIO_TYPE -8 21 | #define ERR_TOO_MANY_ITER -9 22 | 23 | #endif /* !defined(RETURN_H) */ 24 | -------------------------------------------------------------------------------- /pypropep/cpropep/libcpropep/include/type.h: -------------------------------------------------------------------------------- 1 | #ifndef type_h 2 | #define type_h 3 | 4 | #define MAX_PRODUCT 400 /* Maximum species in product */ 5 | #define MAX_ELEMENT 15 /* Maximum different element */ 6 | #define MAX_COMP 20 /* Maximum different ingredient in 7 | composition */ 8 | 9 | #include "compat.h" 10 | 11 | /**************************************************************** 12 | TYPE: Enumeration of the possible state of a substance 13 | *****************************************************************/ 14 | typedef enum 15 | { 16 | GAS, 17 | CONDENSED, 18 | STATE_LAST 19 | } state_t; 20 | 21 | typedef enum 22 | { 23 | TP, /* assign temperature and pressure */ 24 | HP, /* assign enthalpy and pressure */ 25 | SP /* assign entropy and pressure */ 26 | } problem_t; 27 | 28 | typedef enum 29 | { 30 | SUBSONIC_AREA_RATIO, 31 | SUPERSONIC_AREA_RATIO, 32 | PRESSURE 33 | } exit_condition_t; 34 | 35 | 36 | /******************************************** 37 | Note: Specific impulse have unit of m/s 38 | Ns/kg = (kg m / s^2) * (s / kg) 39 | = (m / s) 40 | 41 | It is habitual to found in literature 42 | specific impulse in units of second. 43 | It is in reality Isp/g where g is 44 | the earth acceleration. 45 | **********************************************/ 46 | typedef struct _performance_prop 47 | { 48 | double ae_at; /* Exit aera / Throat aera */ 49 | double a_dotm; /* Exit aera / mass flow rate (m/s/atm) */ 50 | double cstar; /* Characteristic velocity */ 51 | double cf; /* Coefficient of thrust */ 52 | double Ivac; /* Specific impulse (vacuum) */ 53 | double Isp; /* Specific impulse */ 54 | 55 | } performance_prop_t; 56 | 57 | 58 | /*************************************************************** 59 | TYPE: Hold the composition of a specific propellant 60 | ncomp is the number of component 61 | molecule[ ] hold the number in propellant_list corresponding 62 | to the molecule 63 | coef[ ] hold the stochiometric coefficient 64 | 65 | NOTE: It should be great to allocate the memory of the array in 66 | function of the number of element 67 | 68 | DATE: February 6, 2000 69 | ****************************************************************/ 70 | typedef struct _composition 71 | { 72 | short ncomp; /* Number of different component */ 73 | short molecule[MAX_COMP]; /* Molecule code */ 74 | double coef[MAX_COMP]; /* Moles of molecule */ 75 | double density; /* Density of propellant */ 76 | } composition_t; 77 | 78 | 79 | /***************************************************************** 80 | TYPE: Hold the composition of the combustion product. The molecule 81 | are separate between their different possible state. 82 | 83 | NOTE: This structure should be initialize with the function 84 | initialize_product. 85 | 86 | DATE: February 13, 2000 87 | ******************************************************************/ 88 | typedef struct _product 89 | { 90 | int element_listed; /* true if element have been listed */ 91 | int product_listed; /* true if product have been listed */ 92 | int isequil; /* true if equilibrium is ok */ 93 | 94 | /* coefficient matrix for the gases */ 95 | unsigned short A[MAX_ELEMENT][MAX_PRODUCT]; 96 | 97 | short n_element; /* n. of different element */ 98 | short element[MAX_ELEMENT]; /* element list */ 99 | short n[STATE_LAST]; /* n. of species for each state */ 100 | short n_condensed; /* n. of total possible condensed */ 101 | short species[STATE_LAST][MAX_PRODUCT]; /* possible species in each state */ 102 | double coef[STATE_LAST][MAX_PRODUCT]; /* coef. of each molecule */ 103 | 104 | } product_t; 105 | 106 | /* Structure to hold information during the iteration procedure */ 107 | typedef struct _iteration_var 108 | { 109 | double n; /* mol/g of the mixture */ 110 | double ln_n; /* ln(n) */ 111 | double sumn; /* sum of all the nj */ 112 | double delta_ln_n; /* delta ln(n) in the iteration process */ 113 | double delta_ln_T; /* delta ln(T) in the iteration process */ 114 | double delta_ln_nj[MAX_PRODUCT]; /* delta ln(nj) in the iteration process */ 115 | double ln_nj[MAX_PRODUCT]; /* ln(nj) nj are the individual mol/g */ 116 | 117 | } iteration_var_t; 118 | 119 | /********************************************** 120 | Hold information on equilibrium properties once 121 | it have been compute 122 | 123 | June 14, 2000 124 | ***********************************************/ 125 | typedef struct _equilib_prop 126 | { 127 | double P; /* Pressure (atm) */ 128 | double T; /* Temperature (K) */ 129 | double H; /* Enthalpy (kJ/kg) */ 130 | double U; /* Internal energy (kJ/kg) */ 131 | double G; /* Gibbs free energy (kJ/kg) */ 132 | double S; /* Entropy (kJ/(kg)(K)) */ 133 | double M; /* Molar mass (g/mol) */ 134 | double dV_P; /* (d ln(V) / d ln(P))t */ 135 | double dV_T; /* (d ln(V) / d ln(T))p */ 136 | double Cp; /* Specific heat (kJ/(kg)(K)) */ 137 | double Cv; /* Specific heat (kJ/(kg)(K)) */ 138 | double Isex; /* Isentropic exponent (gamma) */ 139 | double Vson; /* Sound speed (m/s) */ 140 | } equilib_prop_t; 141 | 142 | 143 | typedef struct _new_equilibrium 144 | { 145 | int equilibrium_ok; /* true if the equilibrium have been compute */ 146 | int properties_ok; /* true if the properties have been compute */ 147 | int performance_ok; /* true if the performance have been compute */ 148 | 149 | //temporarily 150 | double entropy; 151 | 152 | iteration_var_t itn; 153 | composition_t propellant; 154 | product_t product; 155 | equilib_prop_t properties; 156 | performance_prop_t performance; 157 | 158 | } equilibrium_t; 159 | 160 | 161 | #endif 162 | -------------------------------------------------------------------------------- /pypropep/cpropep/libcpropep/src/Makefile: -------------------------------------------------------------------------------- 1 | 2 | CC = gcc 3 | COPT = -g -Wall -O3 4 | 5 | ROOT = ../.. 6 | 7 | INCLUDEDIR = -I$(ROOT)/libcompat/include \ 8 | -I$(ROOT)/libthermo/include \ 9 | -I$(ROOT)/libnum/include \ 10 | -I../include/ 11 | 12 | DEF = -DGCC #-DTRUE_ARRAY 13 | 14 | LIBNAME = libcpropep.a 15 | 16 | LIBOBJS = equilibrium.o print.o performance.o derivative.o 17 | 18 | all: $(LIBNAME) 19 | 20 | .c.o: 21 | $(CC) $(DEF) $(INCLUDEDIR) $(COPT) -c $*.c -o $*.o 22 | 23 | $(LIBNAME): $(LIBOBJS) 24 | ar -r $@ $(LIBOBJS) 25 | ranlib $@ 26 | mv $(LIBNAME) ../lib 27 | 28 | clean: 29 | rm -f *.o *~ 30 | 31 | deep-clean: clean 32 | rm -f ../lib/$(LIBNAME) 33 | -------------------------------------------------------------------------------- /pypropep/cpropep/libcpropep/src/derivative.c: -------------------------------------------------------------------------------- 1 | /* derivative.c - Fill the mattrix to compute thermochemical derivative 2 | relative to logarithm of pressure and temperature */ 3 | /* $Id: derivative.c,v 1.2 2001/02/22 19:49:28 antoine Exp $ */ 4 | /* Copyright (C) 2000 */ 5 | /* Antoine Lefebvre <antoine.lefebvre@polymtl.ca> */ 6 | /* Mark Pinese <pinese@cyberwizards.com.au> */ 7 | /* */ 8 | /* Licensed under the GPLv2 */ 9 | 10 | 11 | #include <stdlib.h> 12 | #include <stdio.h> 13 | #include <math.h> 14 | 15 | #include "num.h" 16 | 17 | #include "derivative.h" 18 | #include "equilibrium.h" 19 | #include "performance.h" 20 | #include "thermo.h" 21 | 22 | #include "print.h" 23 | #include "compat.h" 24 | #include "return.h" 25 | 26 | int fill_temperature_derivative_matrix(double *matrix, equilibrium_t *e); 27 | int fill_pressure_derivative_matrix(double *matrix, equilibrium_t *e); 28 | 29 | /* Compute the specific_heat of the mixture using thermodynamics 30 | derivative with respect to logarithm of temperature */ 31 | double mixture_specific_heat(equilibrium_t *e, double *sol) 32 | { 33 | short i, j; 34 | double cp, tmp; 35 | 36 | product_t *p = &(e->product); 37 | equilib_prop_t *pr = &(e->properties); 38 | 39 | cp = 0.0; 40 | /* Compute Cp/R */ 41 | for (i = 0; i < p->n_element; i++) 42 | { 43 | tmp = 0.0; 44 | for (j = 0; j < p->n[GAS]; j++) 45 | tmp += p->A[i][j] * p->coef[GAS][j] * 46 | enthalpy_0(p->species[GAS][j], pr->T); 47 | 48 | cp += tmp * sol[i]; 49 | 50 | } 51 | 52 | for (i = 0; i < p->n[CONDENSED]; i++) 53 | { 54 | cp += enthalpy_0(p->species[CONDENSED][i], pr->T) 55 | * sol[i + p->n_element]; 56 | } 57 | 58 | tmp = 0.0; 59 | for (i = 0; i < p->n[GAS]; i++) 60 | { 61 | tmp += p->coef[GAS][i] * enthalpy_0(p->species[GAS][i], pr->T); 62 | } 63 | cp += tmp * sol[p->n_element + p->n[CONDENSED]]; 64 | 65 | cp += mixture_specific_heat_0(e, pr->T); 66 | 67 | for (i = 0; i < p->n[GAS]; i++) 68 | { 69 | cp += p->coef[GAS][i] * pow(enthalpy_0(p->species[GAS][i], pr->T), 2); 70 | } 71 | 72 | return cp; 73 | } 74 | 75 | int derivative(equilibrium_t *e) 76 | { 77 | short size; 78 | double *matrix; 79 | double *sol; 80 | 81 | product_t *p = &(e->product); 82 | equilib_prop_t *prop = &(e->properties); 83 | 84 | 85 | /* the size of the coefficient matrix */ 86 | size = p->n_element + p->n[CONDENSED] + 1; 87 | 88 | matrix = (double *) malloc (size * (size + 1) * sizeof(double)); 89 | 90 | /* allocate the memory for the solution vector */ 91 | sol = (double *) calloc (size, sizeof(double)); 92 | 93 | fill_temperature_derivative_matrix(matrix, e); 94 | 95 | if (NUM_lu(matrix, sol, size) == -1) 96 | { 97 | fprintf(outputfile, "The matrix is singular.\n"); 98 | } 99 | else 100 | { 101 | if (global_verbose > 2) 102 | { 103 | fprintf(outputfile, "Temperature derivative results.\n"); 104 | NUM_print_vec(sol, size); 105 | } 106 | 107 | prop->Cp = mixture_specific_heat(e, sol)*R; 108 | prop->dV_T = 1 + sol[e->product.n_element + e->product.n[CONDENSED]]; 109 | } 110 | 111 | fill_pressure_derivative_matrix(matrix, e); 112 | 113 | if (NUM_lu(matrix, sol, size) == -1) 114 | { 115 | fprintf(outputfile, "The matrix is singular.\n"); 116 | } 117 | else 118 | { 119 | if (global_verbose > 2) 120 | { 121 | fprintf(outputfile, "Pressure derivative results.\n"); 122 | NUM_print_vec(sol, size); 123 | } 124 | prop->dV_P = sol[e->product.n_element + e->product.n[CONDENSED]] - 1; 125 | 126 | } 127 | 128 | prop->Cv = prop->Cp + e->itn.n * R * pow(prop->dV_T, 2)/prop->dV_P; 129 | prop->Isex = -(prop->Cp / prop->Cv) / prop->dV_P; 130 | prop->Vson = sqrt(1000 * e->itn.n * R * e->properties.T * prop->Isex); 131 | 132 | free(matrix); 133 | free(sol); 134 | return 0; 135 | } 136 | 137 | 138 | /* Fill the matrix with the coefficient for evaluating derivatives with 139 | respect to logarithm of temperature at constant pressure */ 140 | int fill_temperature_derivative_matrix(double *matrix, equilibrium_t *e) 141 | { 142 | 143 | short j, k, size; 144 | double tmp; 145 | 146 | short idx_cond, idx_n, idx_T; 147 | 148 | product_t *p = &(e->product); 149 | equilib_prop_t *pr = &(e->properties); 150 | 151 | idx_cond = p->n_element; 152 | idx_n = p->n_element + p->n[CONDENSED]; 153 | idx_T = p->n_element + p->n[CONDENSED] + 1; 154 | 155 | /* the size of the coefficient matrix */ 156 | size = p->n_element + p->n[CONDENSED] + 1; 157 | 158 | /* fill the common part */ 159 | fill_matrix(matrix, e, TP); 160 | 161 | /* del ln(n)/ del ln(T) */ 162 | matrix[idx_n + size * idx_n] = 0.0; 163 | 164 | /* right side */ 165 | for (j = 0; j < p->n_element; j++) 166 | { 167 | tmp = 0.0; 168 | for (k = 0; k < p->n[GAS]; k++) 169 | tmp -= p->A[j][k] * p->coef[GAS][k] * enthalpy_0(p->species[GAS][k], 170 | pr->T); 171 | matrix[j + size * idx_T] = tmp; 172 | } 173 | 174 | for (j = 0; j < p->n[CONDENSED]; j++) /* row */ 175 | matrix[j + idx_cond + size * idx_T] = 176 | -enthalpy_0( p->species[CONDENSED][j], pr->T); 177 | 178 | tmp = 0.0; 179 | for (k = 0; k < p->n[GAS]; k++) 180 | tmp -= p->coef[GAS][k]*enthalpy_0(p->species[GAS][k], pr->T); 181 | 182 | matrix[idx_n + size * idx_T] = tmp; 183 | 184 | return 0; 185 | } 186 | 187 | /* Fill the matrix with the coefficient for evaluating derivatives with 188 | respect to logarithm of pressure at constant temperature */ 189 | int fill_pressure_derivative_matrix(double *matrix, equilibrium_t *e) 190 | { 191 | 192 | short j, k, size; 193 | double tmp; 194 | 195 | short idx_cond, idx_n, idx_T; 196 | 197 | product_t *p = &(e->product); 198 | 199 | idx_cond = p->n_element; 200 | idx_n = p->n_element + p->n[CONDENSED]; 201 | idx_T = p->n_element + p->n[CONDENSED] + 1; 202 | 203 | /* the size of the coefficient matrix */ 204 | size = p->n_element + p->n[CONDENSED] + 1; 205 | 206 | /* fill the common part */ 207 | fill_matrix(matrix, e, TP); 208 | 209 | /* del ln(n)/ del ln(T) */ 210 | matrix[idx_n + size * idx_n] = 0.0; 211 | 212 | /* right side */ 213 | for (j = 0; j < p->n_element; j++) 214 | { 215 | tmp = 0.0; 216 | for (k = 0; k < p->n[GAS]; k++) 217 | tmp += p->A[j][k] * p->coef[GAS][k]; 218 | 219 | matrix[j + size * idx_T] = tmp; 220 | } 221 | 222 | for (j = 0; j < p->n[CONDENSED]; j++) /* row */ 223 | matrix[j + idx_cond + size * idx_T] = 0.0; 224 | 225 | tmp = 0.0; 226 | for (k = 0; k < p->n[GAS]; k++) 227 | tmp += p->coef[GAS][k]; 228 | 229 | matrix[idx_n + size * idx_T] = tmp; 230 | 231 | return 0; 232 | } 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | -------------------------------------------------------------------------------- /pypropep/cpropep/libcpropep/src/performance.c: -------------------------------------------------------------------------------- 1 | /* performance.c - Compute performance caracteristic of a motor 2 | considering equilibrium */ 3 | /* $Id: performance.c,v 1.2 2001/02/22 19:49:28 antoine Exp $ */ 4 | /* Copyright (C) 2000 */ 5 | /* Antoine Lefebvre <antoine.lefebvre@polymtl.ca> */ 6 | /* Mark Pinese <pinese@cyberwizards.com.au> */ 7 | /* */ 8 | /* Licensed under the GPLv2 */ 9 | 10 | #include <stdlib.h> 11 | #include <stdio.h> 12 | #include <math.h> 13 | 14 | #include "performance.h" 15 | #include "derivative.h" 16 | #include "print.h" 17 | #include "equilibrium.h" 18 | 19 | #include "const.h" 20 | #include "compat.h" 21 | #include "return.h" 22 | #include "thermo.h" 23 | 24 | #define TEMP_ITERATION_MAX 8 25 | #define PC_PT_ITERATION_MAX 5 26 | #define PC_PE_ITERATION_MAX 6 27 | 28 | 29 | double compute_temperature(equilibrium_t *e, double pressure, 30 | double p_entropy); 31 | 32 | 33 | /* Entropy of the product at the exit pressure and temperature */ 34 | double product_entropy_exit(equilibrium_t *e, double pressure, double temp) 35 | { 36 | double ent; 37 | double t = e->properties.T; 38 | double p = e->properties.P; 39 | e->properties.T = temp; 40 | e->properties.P = pressure; 41 | ent = product_entropy(e); /* entropy at the new pressure */ 42 | e->properties.T = t; 43 | e->properties.P = p; 44 | return ent; 45 | } 46 | 47 | /* Enthalpy of the product at the exit temperature */ 48 | double product_enthalpy_exit(equilibrium_t *e, double temp) 49 | { 50 | double enth; 51 | double t = e->properties.T; 52 | e->properties.T = temp; 53 | enth = product_enthalpy(e); 54 | e->properties.T = t; 55 | return enth; 56 | } 57 | 58 | /* The temperature could be found by entropy conservation with a 59 | specified pressure */ 60 | double compute_temperature(equilibrium_t *e, double pressure, double p_entropy) 61 | { 62 | int i = 0; 63 | 64 | double delta_lnt; 65 | double temperature; 66 | 67 | /* The first approximation is the chamber temperature */ 68 | temperature = e->properties.T; 69 | 70 | do 71 | { 72 | delta_lnt = (p_entropy - product_entropy_exit(e, pressure, temperature)) 73 | / mixture_specific_heat_0(e, temperature); 74 | 75 | temperature = exp (log(temperature) + delta_lnt); 76 | 77 | i++; 78 | 79 | } while (fabs(delta_lnt) >= 0.5e-4 && i < TEMP_ITERATION_MAX); 80 | 81 | if (i == TEMP_ITERATION_MAX) 82 | { 83 | fprintf(errorfile, 84 | "Temperature do not converge in %d iterations. Don't thrust results.\n", 85 | TEMP_ITERATION_MAX); 86 | } 87 | 88 | return temperature; 89 | } 90 | 91 | int frozen_performance(equilibrium_t *e, exit_condition_t exit_type, 92 | double value) 93 | { 94 | int err_code; 95 | 96 | short i; 97 | 98 | double sound_velocity; 99 | double flow_velocity; 100 | double pc_pt; /* Chamber pressure / Throat pressure */ 101 | double pc_pe; /* Chamber pressure / Exit pressure */ 102 | double log_pc_pe; /* log(pc_pe) */ 103 | double ae_at; /* Exit aera / Throat aera */ 104 | double cp_cv; 105 | double chamber_entropy; 106 | double exit_pressure = 0; 107 | 108 | equilibrium_t *t = e + 1; /* throat equilibrium */ 109 | equilibrium_t *ex = e + 2; /* exit equilibrium */ 110 | 111 | /* find the equilibrium composition in the chamber */ 112 | if (!(e->product.isequil)) 113 | { 114 | if ((err_code = equilibrium(e, HP)) != SUCCESS) 115 | { 116 | fprintf(outputfile, 117 | "No equilibrium, performance evaluation aborted.\n"); 118 | return err_code; 119 | } 120 | } 121 | 122 | /* Simplification due to frozen equilibrium */ 123 | e->properties.dV_T = 1.0; 124 | e->properties.dV_P = -1.0; 125 | 126 | chamber_entropy = product_entropy(e); 127 | 128 | /* begin computation of throat caracteristic */ 129 | copy_equilibrium(t, e); 130 | 131 | cp_cv = e->properties.Cp/e->properties.Cv; 132 | 133 | /* first estimate of Pc/Pt */ 134 | pc_pt = pow (cp_cv/2 + 0.5, cp_cv/(cp_cv - 1)); 135 | 136 | i = 0; 137 | do 138 | { 139 | 140 | t->properties.T = compute_temperature(t, e->properties.P/pc_pt, 141 | chamber_entropy); 142 | 143 | compute_thermo_properties(t); 144 | 145 | sound_velocity = sqrt(1000 * e->itn.n * R * t->properties.T * 146 | t->properties.Isex); 147 | 148 | flow_velocity = sqrt(2000*(product_enthalpy(e)*R*e->properties.T - 149 | product_enthalpy_exit(e, t->properties.T)* 150 | R*t->properties.T)); 151 | 152 | pc_pt = pc_pt / ( 1 + ((pow(flow_velocity, 2) - pow(sound_velocity, 2)) 153 | /(1000*(t->properties.Isex + 1)* 154 | t->itn.n * R *t->properties.T))); 155 | i++; 156 | } while ((fabs((pow(flow_velocity, 2) - pow(sound_velocity, 2)) 157 | /pow(flow_velocity, 2)) > 0.4e-4) && 158 | (i < PC_PT_ITERATION_MAX)); 159 | 160 | if (i == PC_PT_ITERATION_MAX) 161 | { 162 | fprintf(errorfile, 163 | "Throat pressure do not converge in %d iterations. Don't thrust results\n", 164 | PC_PT_ITERATION_MAX); 165 | } 166 | 167 | //printf("%d iterations to evaluate throat pressure.\n", i); 168 | 169 | t->properties.P = e->properties.P/pc_pt; 170 | t->performance.Isp = t->properties.Vson = sound_velocity; 171 | 172 | /* Now compute exit properties */ 173 | copy_equilibrium(ex, e); 174 | 175 | if (exit_type == PRESSURE) 176 | { 177 | exit_pressure = value; 178 | } 179 | else 180 | { 181 | ae_at = value; 182 | 183 | /* Initial estimate of pressure ratio */ 184 | if (exit_type == SUPERSONIC_AREA_RATIO) 185 | { 186 | if ((ae_at > 1.0) && (ae_at < 2.0)) 187 | { 188 | log_pc_pe = log(pc_pt) + sqrt (3.294*pow(ae_at,2) + 1.535*log(ae_at)); 189 | } 190 | else if (ae_at >= 2.0) 191 | { 192 | log_pc_pe = t->properties.Isex + 1.4 * log(ae_at); 193 | } 194 | else 195 | { 196 | printf("Aera ratio out of range ( < 1.0 )\n"); 197 | return ERR_AERA_RATIO; 198 | } 199 | } 200 | else if (exit_type == SUBSONIC_AREA_RATIO) 201 | { 202 | if ((ae_at > 1.0) && (ae_at < 1.09)) 203 | { 204 | log_pc_pe = 0.9 * log(pc_pt) / 205 | (ae_at + 10.587 * pow(log(ae_at), 3) + 9.454 * log(ae_at)); 206 | } 207 | else if (ae_at >= 1.09) 208 | { 209 | log_pc_pe = log(pc_pt) / 210 | (ae_at + 10.587 * pow(log(ae_at), 3) + 9.454 * log(ae_at)); 211 | } 212 | else 213 | { 214 | printf("Aera ratio out of range ( < 1.0 )\n"); 215 | return ERR_AERA_RATIO; 216 | } 217 | } 218 | else 219 | { 220 | return ERR_RATIO_TYPE; 221 | } 222 | 223 | 224 | /* Improved the estimate */ 225 | i = 0; 226 | do 227 | { 228 | pc_pe = exp(log_pc_pe); 229 | ex->properties.P = exit_pressure = e->properties.P/pc_pe; 230 | ex->properties.T = compute_temperature(e, exit_pressure, 231 | chamber_entropy); 232 | 233 | compute_thermo_properties(ex); 234 | 235 | sound_velocity = sqrt(1000 * ex->itn.n * R * ex->properties.T * 236 | ex->properties.Isex); 237 | 238 | ex->performance.Isp = 239 | flow_velocity = sqrt(2000*(product_enthalpy(e)*R*e->properties.T - 240 | product_enthalpy_exit(e, ex->properties.T)* 241 | R*ex->properties.T)); 242 | 243 | ex->performance.ae_at = 244 | (ex->properties.T * t->properties.P * t->performance.Isp) / 245 | (t->properties.T * ex->properties.P * ex->performance.Isp); 246 | 247 | log_pc_pe = log_pc_pe + 248 | (ex->properties.Isex*pow(flow_velocity, 2)/ 249 | (pow(flow_velocity, 2) - pow(sound_velocity,2))) * 250 | (log(ae_at) - log(ex->performance.ae_at)); 251 | 252 | i++; 253 | 254 | } while ( (fabs((log_pc_pe - log(pc_pe))) > 0.00004) && 255 | (i < PC_PE_ITERATION_MAX) ); 256 | 257 | if (i == PC_PE_ITERATION_MAX) 258 | { 259 | fprintf(errorfile, 260 | "Exit pressure do not converge in %d iterations. Don't thrust results\n", 261 | PC_PE_ITERATION_MAX); 262 | } 263 | 264 | //printf("%d iterations to evaluate exit pressure.\n", i); 265 | 266 | pc_pe = exp(log_pc_pe); 267 | exit_pressure = e->properties.P/pc_pe; 268 | 269 | } 270 | 271 | ex->properties.T = compute_temperature(e, exit_pressure, 272 | chamber_entropy); 273 | /* We must check if the exit temperature is more than 50 K lower 274 | than any transition temperature of condensed species. 275 | In this case the results are not good and must be reject. */ 276 | 277 | ex->properties.P = exit_pressure; 278 | ex->performance.Isp = sqrt(2000*(product_enthalpy(e)*R*e->properties.T - 279 | product_enthalpy_exit(e, ex->properties.T)* 280 | R*ex->properties.T)); 281 | 282 | 283 | /* units are (m/s/atm) */ 284 | ex->performance.a_dotm = 1000 * R * ex->properties.T * ex->itn.n / 285 | (ex->properties.P * ex->performance.Isp); 286 | 287 | compute_thermo_properties(ex); 288 | 289 | ex->properties.Vson = sqrt(1000 * e->itn.n * R * ex->properties.T * 290 | e->properties.Isex); 291 | 292 | 293 | t->performance.a_dotm = 1000 * R * t->properties.T 294 | * t->itn.n / (t->properties.P * t->performance.Isp); 295 | t->performance.ae_at = 1.0; 296 | t->performance.cstar = e->properties.P * t->performance.a_dotm; 297 | t->performance.cf = t->performance.Isp / 298 | (e->properties.P * t->performance.a_dotm); 299 | t->performance.Ivac = t->performance.Isp + t->properties.P 300 | * t->performance.a_dotm; 301 | 302 | ex->performance.ae_at = 303 | (ex->properties.T * t->properties.P * t->performance.Isp) / 304 | (t->properties.T * ex->properties.P * ex->performance.Isp); 305 | ex->performance.cstar = e->properties.P * t->performance.a_dotm; 306 | ex->performance.cf = ex->performance.Isp / 307 | (e->properties.P * t->performance.a_dotm); 308 | ex->performance.Ivac = ex->performance.Isp + ex->properties.P 309 | * ex->performance.a_dotm; 310 | 311 | return SUCCESS; 312 | } 313 | 314 | 315 | int shifting_performance(equilibrium_t *e, exit_condition_t exit_type, 316 | double value) 317 | { 318 | int err_code; 319 | short i; 320 | double sound_velocity = 0.0; 321 | double flow_velocity; 322 | double pc_pt; 323 | double pc_pe; 324 | double log_pc_pe; 325 | double ae_at; 326 | double chamber_entropy; 327 | double exit_pressure = 0; 328 | 329 | equilibrium_t *t = e + 1; /* throat equilibrium */ 330 | equilibrium_t *ex = e + 2; /* throat equilibrium */ 331 | 332 | /* find the equilibrium composition in the chamber */ 333 | if (!(e->product.isequil)) 334 | /* if the equilibrium have not already been compute */ 335 | { 336 | if ((err_code = equilibrium(e, HP)) < 0) 337 | { 338 | fprintf(outputfile, "No equilibrium, performance evaluation aborted.\n"); 339 | return err_code; 340 | } 341 | } 342 | 343 | /* Begin by first aproximate the new equilibrium to be 344 | the same as the chamber equilibrium */ 345 | 346 | copy_equilibrium(t, e); 347 | 348 | chamber_entropy = product_entropy(e); 349 | 350 | /* Computing throat condition */ 351 | /* Approximation of the throat pressure */ 352 | pc_pt = pow(t->properties.Isex/2 + 0.5, 353 | t->properties.Isex/(t->properties.Isex - 1) ); 354 | 355 | t->entropy = chamber_entropy; 356 | 357 | i = 0; 358 | do 359 | { 360 | t->properties.P = e->properties.P/pc_pt; 361 | 362 | /* We must compute the new equilibrium each time */ 363 | if ((err_code = equilibrium(t, SP)) < 0) 364 | { 365 | fprintf(outputfile, "No equilibrium, performance evaluation aborted.\n"); 366 | return err_code; 367 | } 368 | 369 | sound_velocity = sqrt (1000*t->itn.n*R*t->properties.T* 370 | t->properties.Isex); 371 | 372 | flow_velocity = sqrt (2000*(product_enthalpy(e)*R*e->properties.T - 373 | product_enthalpy(t)*R*t->properties.T)); 374 | 375 | pc_pt = pc_pt / ( 1 + ((pow(flow_velocity, 2) - pow(sound_velocity, 2)) 376 | /(1000*(t->properties.Isex + 1)*t->itn.n*R* 377 | t->properties.T))); 378 | i++; 379 | } while ((fabs((pow(flow_velocity, 2) - pow(sound_velocity, 2)) 380 | /pow(flow_velocity, 2)) > 0.4e-4) && 381 | (i < PC_PT_ITERATION_MAX)); 382 | 383 | if (i == PC_PT_ITERATION_MAX) 384 | { 385 | fprintf(errorfile, "Throat pressure do not converge in %d iterations." 386 | " Don't thrust results.\n", PC_PT_ITERATION_MAX); 387 | } 388 | 389 | //printf("%d iterations to evaluate throat pressure.\n", i); 390 | 391 | t->properties.P = e->properties.P/pc_pt; 392 | t->properties.Vson = sound_velocity; 393 | t->performance.Isp = sound_velocity; 394 | 395 | 396 | t->performance.a_dotm = 1000 * R * 397 | t->properties.T * t->itn.n / 398 | (t->properties.P * t->performance.Isp); 399 | 400 | copy_equilibrium(ex, e); 401 | 402 | if (exit_type == PRESSURE) 403 | { 404 | exit_pressure = value; 405 | } 406 | else 407 | { 408 | ae_at = value; 409 | 410 | /* Initial estimate of pressure ratio */ 411 | if (exit_type == SUPERSONIC_AREA_RATIO) 412 | { 413 | if ((ae_at > 1.0) && (ae_at < 2.0)) 414 | { 415 | log_pc_pe = log(pc_pt) + sqrt (3.294*pow(ae_at,2) + 1.535*log(ae_at)); 416 | } 417 | else if (ae_at >= 2.0) 418 | { 419 | log_pc_pe = t->properties.Isex + 1.4 * log(ae_at); 420 | } 421 | else 422 | { 423 | printf("Aera ratio out of range ( < 1.0 )\n"); 424 | return ERR_AERA_RATIO; 425 | } 426 | } 427 | else if (exit_type == SUBSONIC_AREA_RATIO) 428 | { 429 | if ((ae_at > 1.0) && (ae_at < 1.09)) 430 | { 431 | log_pc_pe = 0.9 * log(pc_pt) / 432 | (ae_at + 10.587 * pow(log(ae_at), 3) + 9.454 * log(ae_at)); 433 | } 434 | else if (ae_at >= 1.09) 435 | { 436 | log_pc_pe = log(pc_pt) / 437 | (ae_at + 10.587 * pow(log(ae_at), 3) + 9.454 * log(ae_at)); 438 | } 439 | else 440 | { 441 | printf("Aera ratio out of range ( < 1.0 )\n"); 442 | return ERR_AERA_RATIO; 443 | } 444 | } 445 | else 446 | { 447 | return ERR_RATIO_TYPE; 448 | } 449 | 450 | 451 | /* Improved the estimate */ 452 | ex->entropy = chamber_entropy; 453 | i = 0; 454 | do 455 | { 456 | pc_pe = exp(log_pc_pe); 457 | ex->properties.P = exit_pressure = e->properties.P/pc_pe; 458 | 459 | 460 | /* Find the exit equilibrium */ 461 | if ((err_code = equilibrium(ex, SP)) < 0) 462 | { 463 | fprintf(outputfile, 464 | "No equilibrium, performance evaluation aborted.\n"); 465 | return err_code; 466 | } 467 | 468 | sound_velocity = ex->properties.Vson; 469 | 470 | ex->performance.Isp = 471 | flow_velocity = sqrt(2000*(product_enthalpy(e)*R*e->properties.T - 472 | product_enthalpy(ex)*R*ex->properties.T)); 473 | 474 | ex->performance.ae_at = 475 | (ex->properties.T * t->properties.P * t->performance.Isp) / 476 | (t->properties.T * ex->properties.P * ex->performance.Isp); 477 | 478 | log_pc_pe = log_pc_pe + 479 | (ex->properties.Isex*pow(flow_velocity, 2)/ 480 | (pow(flow_velocity, 2) - pow(sound_velocity,2))) * 481 | (log(ae_at) - log(ex->performance.ae_at)); 482 | i++; 483 | } while ((fabs((log_pc_pe - log(pc_pe))) > 0.00004) && 484 | (i < PC_PE_ITERATION_MAX)); 485 | 486 | if (i == PC_PE_ITERATION_MAX) 487 | { 488 | fprintf(errorfile, "Exit pressure do not converge in %d iteration." 489 | " Don't thrust results.\n", PC_PE_ITERATION_MAX); 490 | } 491 | 492 | //printf("%d iterations to evaluate exit pressure.\n", i); 493 | 494 | pc_pe = exp(log_pc_pe); 495 | exit_pressure = e->properties.P/pc_pe; 496 | } 497 | 498 | ex->entropy = chamber_entropy; 499 | ex->properties.P = exit_pressure; 500 | 501 | /* Find the exit equilibrium */ 502 | if ((err_code = equilibrium(ex, SP)) < 0) 503 | { 504 | fprintf(outputfile, "No equilibrium, performance evaluation aborted.\n"); 505 | return err_code; 506 | } 507 | 508 | flow_velocity = sqrt(2000*(product_enthalpy(e)*R*e->properties.T - 509 | product_enthalpy(ex)*R*ex->properties.T)); 510 | 511 | 512 | ex->performance.Isp = flow_velocity; 513 | 514 | ex->performance.a_dotm = 1000 * R * 515 | ex->properties.T * ex->itn.n / 516 | (ex->properties.P * ex->performance.Isp); 517 | 518 | t->performance.ae_at = 1.0; 519 | t->performance.cstar = e->properties.P * t->performance.a_dotm; 520 | t->performance.cf = t->performance.Isp / 521 | (e->properties.P * t->performance.a_dotm); 522 | t->performance.Ivac = t->performance.Isp + t->properties.P 523 | * t->performance.a_dotm; 524 | 525 | ex->performance.ae_at = 526 | (ex->properties.T * t->properties.P * t->performance.Isp) / 527 | (t->properties.T * ex->properties.P * ex->performance.Isp); 528 | ex->performance.cstar = e->properties.P * t->performance.a_dotm; 529 | ex->performance.cf = ex->performance.Isp / 530 | (e->properties.P * t->performance.a_dotm); 531 | ex->performance.Ivac = ex->performance.Isp + ex->properties.P 532 | * ex->performance.a_dotm; 533 | 534 | return SUCCESS; 535 | } 536 | 537 | 538 | 539 | 540 | 541 | -------------------------------------------------------------------------------- /pypropep/cpropep/libcpropep/src/print.c: -------------------------------------------------------------------------------- 1 | /* print.c - Output functions */ 2 | /* $Id: print.c,v 1.2 2001/02/22 19:49:28 antoine Exp $ */ 3 | /* Copyright (C) 2000 */ 4 | /* Antoine Lefebvre <antoine.lefebvre@polymtl.ca> */ 5 | /* Mark Pinese <pinese@cyberwizards.com.au> */ 6 | /* */ 7 | /* Licensed under the GPLv2 */ 8 | 9 | #include <stdio.h> 10 | 11 | #include "print.h" 12 | #include "performance.h" 13 | #include "equilibrium.h" 14 | #include "conversion.h" 15 | #include "thermo.h" 16 | #include "const.h" 17 | 18 | char header[][32] = { 19 | "CHAMBER", 20 | "THROAT", 21 | "EXIT" }; 22 | 23 | char err_message[][64] = { 24 | "Error allocating memory", 25 | "Error opening file", 26 | "Error EOF", 27 | "Error memory not allocated", 28 | "Error too much product", 29 | "Error in equilibrium", 30 | "Error bad aera ratio", 31 | "Error bad aera ratio type"}; 32 | 33 | FILE * errorfile; 34 | FILE * outputfile; 35 | 36 | int print_error_message(int error_code) 37 | { 38 | fprintf(errorfile, "%s\n", err_message[-error_code - 1]); 39 | return 0; 40 | } 41 | 42 | int print_propellant_info(int sp) 43 | { 44 | int j; 45 | 46 | if (sp > num_propellant || sp < 0) 47 | return -1; 48 | 49 | fprintf(outputfile, "Code %-35s Enthalpy Density Composition\n", "Name"); 50 | fprintf(outputfile, "%d %-35s % .4f % .2f", sp, 51 | (propellant_list + sp)->name, 52 | (propellant_list + sp)->heat, 53 | (propellant_list + sp)->density); 54 | 55 | fprintf(outputfile, " "); 56 | 57 | /* print the composition */ 58 | for (j = 0; j < 6; j++) 59 | { 60 | if (!((propellant_list + sp)->coef[j] == 0)) 61 | fprintf(outputfile, "%d%s ", (propellant_list + sp)->coef[j], 62 | symb[ (propellant_list + sp)->elem[j] ]); 63 | } 64 | fprintf(outputfile, "\n"); 65 | return 0; 66 | } 67 | 68 | int print_thermo_info(int sp) 69 | { 70 | int i, j; 71 | thermo_t *s; 72 | 73 | if (sp > num_thermo || sp < 0) 74 | return -1; 75 | 76 | s = (thermo_list + sp); 77 | 78 | fprintf(outputfile, "---------------------------------------------\n"); 79 | fprintf(outputfile, "Name: \t\t\t%s\n", s->name); 80 | fprintf(outputfile, "Comments: \t\t%s\n", s->comments); 81 | fprintf(outputfile, "Id: \t\t\t%s\n", s->id); 82 | fprintf(outputfile, "Chemical formula:\t"); 83 | 84 | for (i = 0; i < 5; i++) 85 | { 86 | if (!(s->coef[i] == 0)) 87 | fprintf(outputfile, "%d%s", s->coef[i], symb[ s->elem[i]]); 88 | } 89 | fprintf(outputfile, "\n"); 90 | fprintf(outputfile, "State:\t\t\t"); 91 | switch (s->state) 92 | { 93 | case GAS: 94 | fprintf(outputfile, "GAZ\n"); 95 | break; 96 | case CONDENSED: 97 | fprintf(outputfile, "CONDENSED\n"); 98 | break; 99 | default: 100 | printf("UNRECOGNIZE\n"); 101 | } 102 | 103 | fprintf(outputfile, "\n"); 104 | fprintf(outputfile, "Molecular weight: \t\t% f g/mol\n", s->weight); 105 | fprintf(outputfile, "Heat of formation at 298.15 K : % f J/mol\n", s->heat); 106 | fprintf(outputfile, "Assign enthalpy : % f J/mol\n", s->enth); 107 | fprintf(outputfile, "HO(298.15) - HO(0): \t\t% f J/mol\n", s->dho); 108 | fprintf(outputfile, "Number of temperature range: % d\n\n", s->nint); 109 | 110 | for (i = 0; i < s->nint; i++) 111 | { 112 | fprintf(outputfile, "Interval: %f - %f \n", s->range[i][0], 113 | s->range[i][1]); 114 | for (j = 0; j < 9; j++) 115 | fprintf(outputfile, "% .9e ", s->param[i][j]); 116 | fprintf(outputfile, "\n\n"); 117 | } 118 | fprintf(outputfile, "---------------------------------------------\n"); 119 | return 0; 120 | } 121 | 122 | 123 | int print_thermo_list(void) 124 | { 125 | int i; 126 | for (i = 0; i < num_thermo; i++) 127 | fprintf(outputfile, "%-4d %-15s % .2f\n", i, (thermo_list + i)->name, 128 | (thermo_list + i)->heat); 129 | 130 | return 0; 131 | } 132 | 133 | int print_propellant_list(void) 134 | { 135 | int i; 136 | for (i = 0; i < num_propellant; i++) 137 | fprintf(outputfile, "%-4d %-30s %5f\n", i, (propellant_list + i)->name, 138 | (propellant_list +i)->heat); 139 | 140 | return 0; 141 | } 142 | 143 | 144 | int print_condensed(product_t p) 145 | { 146 | int i; 147 | for (i = 0; i < p.n[CONDENSED]; i ++) 148 | fprintf(outputfile, "%s ", 149 | (thermo_list + p.species[CONDENSED][i])->name ); 150 | fprintf(outputfile, "\n"); 151 | return 0; 152 | } 153 | 154 | int print_gazeous(product_t p) 155 | { 156 | int i; 157 | for (i = 0; i < p.n[GAS]; i++) 158 | fprintf(outputfile, "%s ", (thermo_list + p.species[GAS][i])->name); 159 | fprintf(outputfile, "\n"); 160 | return 0; 161 | } 162 | 163 | int print_product_composition(equilibrium_t *e, short npt) 164 | { 165 | int i, j, k; 166 | 167 | double mol_g = e->itn.n; 168 | 169 | /* we have to build a list of all condensed species present 170 | in the three equilibrium */ 171 | int n = 0; 172 | int condensed_list[MAX_PRODUCT]; 173 | 174 | /* ok become false if the species already exist in the list */ 175 | int ok = 1; 176 | 177 | double qt; 178 | 179 | for (i = 0; i < e->product.n[CONDENSED]; i++) 180 | mol_g += e->product.coef[CONDENSED][i]; 181 | 182 | fprintf(outputfile, "\nMolar fractions\n\n"); 183 | for (i = 0; i < e->product.n[GAS]; i++) 184 | { 185 | if (e->product.coef[GAS][i]/e->itn.n > 0.0) 186 | { 187 | fprintf(outputfile, "%-20s", 188 | (thermo_list + e->product.species[GAS][i])->name); 189 | 190 | for (j = 0; j < npt; j++) 191 | fprintf(outputfile, " %11.4e", (e+j)->product.coef[GAS][i]/mol_g); 192 | fprintf(outputfile,"\n"); 193 | 194 | } 195 | } 196 | 197 | /* build the list of condensed */ 198 | for (i = 0; i < npt; i++) 199 | { 200 | for (j = 0; j < (e+i)->product.n[CONDENSED]; j++) 201 | { 202 | for (k = 0; k < n; k++) 203 | { 204 | /* check if the condensed are to be include in the list */ 205 | if (condensed_list[k] == (e+i)->product.species[CONDENSED][j]) 206 | { 207 | /* we do not have to include the species */ 208 | ok = 0; 209 | break; 210 | } 211 | } /* k */ 212 | 213 | if (ok) 214 | { 215 | condensed_list[n] = (e+i)->product.species[CONDENSED][j]; 216 | n++; 217 | } 218 | 219 | /* reset the flag */ 220 | ok = 1; 221 | } /* j */ 222 | } /* i */ 223 | 224 | 225 | if (n > 0) 226 | { 227 | fprintf(outputfile, "Condensed species\n"); 228 | for (i = 0; i < n; i++) 229 | { 230 | fprintf(outputfile, "%-20s", 231 | (thermo_list + condensed_list[i])->name); 232 | 233 | for (j = 0; j < npt; j++) 234 | { 235 | /* search in the product of each equilibrium if the 236 | condensed is present */ 237 | 238 | qt = 0.0; 239 | 240 | for (k = 0; k < (e+j)->product.n[CONDENSED]; k++) 241 | { 242 | if (condensed_list[i] == (e+j)->product.species[CONDENSED][k]) 243 | { 244 | qt = (e+j)->product.coef[CONDENSED][k]; 245 | break; 246 | } 247 | } 248 | 249 | fprintf(outputfile, " %11.4e", qt/mol_g); 250 | } 251 | fprintf(outputfile,"\n"); 252 | 253 | } 254 | } 255 | fprintf(outputfile, "\n"); 256 | return 0; 257 | } 258 | 259 | 260 | int print_propellant_composition(equilibrium_t *e) 261 | { 262 | int i, j; 263 | 264 | fprintf(outputfile, "Propellant composition\n"); 265 | fprintf(outputfile, "Code %-35s mol Mass (g) Composition\n", "Name"); 266 | for (i = 0; i < e->propellant.ncomp; i++) 267 | { 268 | fprintf(outputfile, "%-4d %-35s %.4f %.4f ", e->propellant.molecule[i], 269 | PROPELLANT_NAME(e->propellant.molecule[i]), e->propellant.coef[i], 270 | e->propellant.coef[i] * 271 | propellant_molar_mass(e->propellant.molecule[i])); 272 | 273 | fprintf(outputfile, " "); 274 | /* print the composition */ 275 | for (j = 0; j < 6; j++) 276 | { 277 | if (!((propellant_list + e->propellant.molecule[i])->coef[j] == 0)) 278 | fprintf(outputfile, "%d%s ", 279 | (propellant_list + e->propellant.molecule[i])->coef[j], 280 | symb[(propellant_list + e->propellant.molecule[i])->elem[j]]); 281 | } 282 | fprintf(outputfile, "\n"); 283 | } 284 | fprintf(outputfile, "Density : % .3f g/cm^3\n", e->propellant.density); 285 | 286 | if (e->product.element_listed) 287 | { 288 | fprintf(outputfile, "%d different elements\n", e->product.n_element); 289 | /* Print those elements */ 290 | for (i = 0; i < e->product.n_element; i++) 291 | fprintf(outputfile, "%s ", symb[e->product.element[i]] ); 292 | fprintf(outputfile, "\n"); 293 | } 294 | 295 | fprintf(outputfile, "Total mass: % f g\n", propellant_mass(e)); 296 | 297 | fprintf(outputfile, "Enthalpy : % .2f kJ/kg\n", 298 | propellant_enthalpy(e)); 299 | 300 | fprintf(outputfile, "\n"); 301 | 302 | if (e->product.product_listed) 303 | { 304 | fprintf(outputfile, "%d possible gazeous species\n", e->product.n[GAS]); 305 | if (global_verbose > 1) 306 | print_gazeous(e->product); 307 | fprintf(outputfile, "%d possible condensed species\n\n", 308 | e->product.n_condensed); 309 | if (global_verbose > 1) 310 | print_condensed(e->product); 311 | } 312 | 313 | return 0; 314 | } 315 | 316 | int print_performance_information (equilibrium_t *e, short npt) 317 | { 318 | short i; 319 | 320 | fprintf(outputfile, "Ae/At : "); 321 | for (i = 1; i < npt; i++) 322 | fprintf(outputfile, " % 11.5f", (e+i)->performance.ae_at); 323 | fprintf(outputfile, "\n"); 324 | 325 | fprintf(outputfile, "A/dotm (m/s/atm) : "); 326 | for (i = 1; i < npt; i++) 327 | fprintf(outputfile, " % 11.5f", (e+i)->performance.a_dotm); 328 | fprintf(outputfile, "\n"); 329 | 330 | fprintf(outputfile, "C* (m/s) : "); 331 | for (i = 1; i < npt; i++) 332 | fprintf(outputfile, " % 11.5f", (e+i)->performance.cstar); 333 | fprintf(outputfile, "\n"); 334 | 335 | fprintf(outputfile, "Cf : "); 336 | for (i = 1; i < npt; i++) 337 | fprintf(outputfile, " % 11.5f", (e+i)->performance.cf); 338 | fprintf(outputfile, "\n"); 339 | 340 | fprintf(outputfile, "Ivac (m/s) : "); 341 | for (i = 1; i < npt; i++) 342 | fprintf(outputfile, " % 11.5f", (e+i)->performance.Ivac); 343 | fprintf(outputfile, "\n"); 344 | 345 | fprintf(outputfile, "Isp (m/s) : "); 346 | for (i = 1; i < npt; i++) 347 | fprintf(outputfile, " % 11.5f", (e+i)->performance.Isp); 348 | fprintf(outputfile, "\n"); 349 | 350 | fprintf(outputfile, "Isp/g (s) : "); 351 | for (i = 1; i < npt; i++) 352 | fprintf(outputfile, " % 11.5f", (e+i)->performance.Isp/Ge); 353 | fprintf(outputfile, "\n"); 354 | 355 | return 0; 356 | } 357 | 358 | 359 | int print_product_properties(equilibrium_t *e, short npt) 360 | { 361 | short i; 362 | 363 | fprintf(outputfile, " "); 364 | for (i = 0; i < npt; i++) 365 | fprintf(outputfile, " %11s", header[i]); 366 | fprintf(outputfile, "\n"); 367 | 368 | fprintf(outputfile, "Pressure (atm) :"); 369 | for (i = 0; i < npt; i++) 370 | fprintf(outputfile, " % 11.3f", (e+i)->properties.P); 371 | fprintf(outputfile, "\n"); 372 | fprintf(outputfile, "Temperature (K) :"); 373 | for (i = 0; i < npt; i++) 374 | fprintf(outputfile, " % 11.3f", (e+i)->properties.T); 375 | fprintf(outputfile, "\n"); 376 | fprintf(outputfile, "H (kJ/kg) :"); 377 | for (i = 0; i < npt; i++) 378 | fprintf(outputfile, " % 11.3f", (e+i)->properties.H); 379 | fprintf(outputfile, "\n"); 380 | fprintf(outputfile, "U (kJ/kg) :"); 381 | for (i = 0; i < npt; i++) 382 | fprintf(outputfile, " % 11.3f", (e+i)->properties.U); 383 | fprintf(outputfile, "\n"); 384 | fprintf(outputfile, "G (kJ/kg) :"); 385 | for (i = 0; i < npt; i++) 386 | fprintf(outputfile, " % 11.3f", (e+i)->properties.G); 387 | fprintf(outputfile, "\n"); 388 | fprintf(outputfile, "S (kJ/(kg)(K) :"); 389 | for (i = 0; i < npt; i++) 390 | fprintf(outputfile, " % 11.3f", (e+i)->properties.S); 391 | fprintf(outputfile, "\n"); 392 | fprintf(outputfile, "M (g/mol) :"); 393 | for (i = 0; i < npt; i++) 394 | fprintf(outputfile, " % 11.3f", (e+i)->properties.M); 395 | fprintf(outputfile, "\n"); 396 | 397 | fprintf(outputfile, "(dLnV/dLnP)t :"); 398 | for (i = 0; i < npt; i++) 399 | fprintf(outputfile, " % 11.5f", (e+i)->properties.dV_P); 400 | fprintf(outputfile, "\n"); 401 | fprintf(outputfile, "(dLnV/dLnT)p :"); 402 | for (i = 0; i < npt; i++) 403 | fprintf(outputfile, " % 11.5f", (e+i)->properties.dV_T); 404 | fprintf(outputfile, "\n"); 405 | fprintf(outputfile, "Cp (kJ/(kg)(K)) :"); 406 | for (i = 0; i < npt; i++) 407 | fprintf(outputfile, " % 11.5f", (e+i)->properties.Cp); 408 | fprintf(outputfile, "\n"); 409 | fprintf(outputfile, "Cv (kJ/(kg)(K)) :"); 410 | for (i = 0; i < npt; i++) 411 | fprintf(outputfile, " % 11.5f", (e+i)->properties.Cv); 412 | fprintf(outputfile, "\n"); 413 | fprintf(outputfile, "Cp/Cv :"); 414 | for (i = 0; i < npt; i++) 415 | fprintf(outputfile, " % 11.5f", (e+i)->properties.Cp/(e+i)->properties.Cv); 416 | fprintf(outputfile, "\n"); 417 | fprintf(outputfile, "Gamma :"); 418 | for (i = 0; i < npt; i++) 419 | fprintf(outputfile, " % 11.5f", (e+i)->properties.Isex); 420 | fprintf(outputfile, "\n"); 421 | fprintf(outputfile, "Vson (m/s) :"); 422 | for (i = 0; i < npt; i++) 423 | fprintf(outputfile, " % 11.5f", (e+i)->properties.Vson); 424 | fprintf(outputfile, "\n"); 425 | fprintf(outputfile, "\n"); 426 | return 0; 427 | } 428 | -------------------------------------------------------------------------------- /pypropep/cpropep/libnum/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: 3 | @mkdir -p lib 4 | make -C src all 5 | 6 | 7 | clean: 8 | @rm -rf lib 9 | make -C src clean 10 | 11 | deep-clean: clean 12 | make -C src deep-clean 13 | 14 | -------------------------------------------------------------------------------- /pypropep/cpropep/libnum/Makefile.win: -------------------------------------------------------------------------------- 1 | CC = bcc32 2 | CPP32 = cpp32 3 | LIBRARIAN = tlib 4 | LINKER = ilink32 5 | RC = brc32 6 | 7 | 8 | COPT = -3 -O2 -w-8012 -w-8004 -w-8057 -IC:\borland\bcc55\include 9 | OBJS = lu.obj rk4.obj general.obj print.obj sec.obj 10 | 11 | TLIBNUM = +lu.obj +rk4.obj +general.obj +print.obj +sec.obj 12 | 13 | LDOPT = -LC:\borland\bcc55\lib 14 | 15 | PROG = test.exe 16 | PROGOBJS = test.obj 17 | 18 | LIBNUM = libnum.lib 19 | 20 | .SUFFIXES: .c 21 | 22 | all: $(LIBNUM) $(PROG) 23 | 24 | .c.obj: 25 | $(CC) $(DEF) $(COPT) -c $*.c -o $*.obj 26 | 27 | $(LIBNUM): $(OBJS) 28 | tlib $@ $(TLIBNUM) 29 | 30 | $(PROG): $(PROGOBJS) 31 | $(CC) $(LDOPT) $(LIBNUM) $(PROGOBJS) 32 | 33 | clean: 34 | del *.obj 35 | del *.bak 36 | del *.tds 37 | 38 | deep-clean: clean 39 | del $(LIBNUM) 40 | del $(PROG) 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /pypropep/cpropep/libnum/include/num.h: -------------------------------------------------------------------------------- 1 | /* num.h - Library of numerical method 2 | * $Id: num.h,v 1.3 2001/02/22 19:47:37 antoine Exp $ 3 | * Copyright (C) 2000 4 | * Antoine Lefebvre <antoine.lefebvre@polymtl.ca> 5 | * 6 | * Licensed under the GPL 7 | */ 8 | 9 | #ifndef num_h 10 | #define num_h 11 | 12 | /* NOTE on matrix representation 13 | * ----------------------------- 14 | * All matrix should be allocate the following way 15 | * 16 | * matrix = (double *) malloc (sizeof(double) * line * column) 17 | * 18 | * to access the value at line 2, column 4, you do it like that 19 | * 20 | * matrix[2 + 4*line] 21 | */ 22 | 23 | 24 | /* This type is used as function pointer for the 25 | * sysnewton algorithm. 26 | */ 27 | typedef double (*func_t)(double *x); 28 | 29 | typedef struct status 30 | { 31 | 32 | int itn; 33 | 34 | } status_t; 35 | 36 | #define NO_CONVERGENCE 1 37 | #define NO_SOLUTION 2 38 | 39 | 40 | /* Find the solution of linear system of equation using the 41 | * LU factorisation method as explain in 42 | * 'Advanced engineering mathematics' bye Erwin Kreyszig. 43 | * 44 | * This algorithm will also do column permutation to find 45 | * the larger pivot. 46 | * 47 | * ARGUMENTS 48 | * --------- 49 | * matrix: the augmented matrix of coefficient in the system 50 | * with right hand side value. 51 | * 52 | * solution: the solution vector 53 | * 54 | * neq: number of equation in the system 55 | * 56 | * Antoine Lefebvre 57 | * february 6, 2000 Initial version 58 | * october 20, 2000 revision of the permutation method 59 | */ 60 | int NUM_lu(double *matrix, double *solution, int neq); 61 | //int old_lu(double *matrix, double *solution, int neq); 62 | 63 | /* This function print the coefficient of the matrix to 64 | * the screen. 65 | * 66 | * matrix: should be an augmented matrix 67 | * neq : number of equation 68 | */ 69 | int NUM_print_matrix(double *matrix, int neq); 70 | 71 | /* Print the coefficient of the square matrix to the screen 72 | */ 73 | int NUM_print_square_matrix(double *matrix, int neq); 74 | 75 | 76 | /* This function print the contents of the vector to 77 | * the screen. 78 | * 79 | * vec: vector containing neq element 80 | */ 81 | int NUM_print_vec(double *vec, int neq); 82 | 83 | 84 | /************************************************************** 85 | FUNCTION: This function solve systems of ODE of the first order 86 | with the Runge-Kutta method of the fourth order. 87 | 88 | PARAMETER: The first parameter is a pointer to the function we 89 | we want to solve. This function take five parameter 90 | neq is the number of equations in the system 91 | time is the time at which we want to evaluate 92 | y is an array containing an initial value 93 | dy will store the result of the function 94 | ierr any error field 95 | 96 | step is the time variation 97 | duration is the total time of the simulation 98 | ic are the initial conditions 99 | **y is an array containing all the data 100 | 101 | void * is nay user data 102 | 103 | COMMENTS: **y must be properly allocated, [number of points]X[neq] 104 | 105 | It could be interesting to add a tolerance and use 106 | variable step to reach our tolerance instead of using a 107 | fixed step. 108 | 109 | AUTHOR: Antoine Lefebvre 110 | 111 | DATE: February 11 112 | *****************************************************************/ 113 | int NUM_rk4(int (*f)(int neq, double time, double *y, double *dy, void *data), 114 | int neq, double step, double duration, double *ic, 115 | double **y, void *data ); 116 | 117 | int NUM_rkf(int (*f)(int neq, double time, double *y, double *dy, void *data), 118 | int neq, double step, double duration, double *ic, 119 | double **y, double epsil, void *data); 120 | 121 | /* this function return the nearest integer to a */ 122 | /* it is a replacement of rint which is not ANSI complient */ 123 | int Round(double a); 124 | 125 | double epsilon(void); 126 | 127 | int NUM_sec(double (*f)(double x), double x0, double x1, int nmax, 128 | double epsilon, double *ans); 129 | 130 | int NUM_newton(double (*f)(double x), double (*df)(double x), double x0, 131 | int nmax, double epsilon, double *ans); 132 | 133 | int NUM_ptfix(double (*f)(double x), double x0, 134 | double nmax, double epsilon, double *ans); 135 | 136 | int NUM_sysnewton(func_t *Jac, func_t *R, double *x, int nvar, 137 | int nmax, double eps); 138 | 139 | 140 | int trapeze(double *data, int n_point, int col, int off, double *integral); 141 | 142 | int simpson(double *data, int n_point, int col, int off, double *integral); 143 | 144 | int create_spline(double *data, int n_point, double *spline); 145 | 146 | #endif 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /pypropep/cpropep/libnum/src/Makefile: -------------------------------------------------------------------------------- 1 | 2 | CC = gcc 3 | COPT = -g -Wall -O3 #-pg 4 | 5 | LIB = -lnum -lm 6 | LIBDIR = -L../lib/ 7 | INCDIR = -I../include/ 8 | 9 | DEF = -DLINUX 10 | 11 | PROG = test 12 | OBJS = test.o 13 | 14 | LIBOBJS = lu.o rk4.o rkf.o general.o print.o sec.o newton.o ptfix.o\ 15 | sysnewton.o trapeze.o simpson.o spline.o 16 | 17 | LIBNUM = libnum.a 18 | 19 | all: $(LIBNUM) $(PROG) 20 | 21 | .c.o: 22 | $(CC) $(DEF) $(INCDIR) $(COPT) -c $*.c -o $*.o 23 | 24 | $(LIBNUM): $(LIBOBJS) 25 | ar -r $@ $(LIBOBJS) 26 | ranlib $@ 27 | mv $(LIBNUM) ../lib/ 28 | 29 | $(PROG): $(LIBNUM) $(OBJS) 30 | $(CC) $(COPT) $(OBJS) $(LIBDIR) $(LIB) -o $@ 31 | 32 | 33 | clean: 34 | rm -f $(PROG) *.o *~ 35 | 36 | deep-clean: clean 37 | rm -f ../lib/$(LIBNUM) 38 | 39 | -------------------------------------------------------------------------------- /pypropep/cpropep/libnum/src/general.c: -------------------------------------------------------------------------------- 1 | 2 | #include "num.h" 3 | 4 | int Round(double a) 5 | { 6 | int t = a; 7 | 8 | if (a - (double)t < 0.5) 9 | return t; 10 | else if (a - (double)t > 0.5) 11 | return t + 1; 12 | else 13 | return (t + (t % 2)); 14 | } 15 | 16 | 17 | double epsilon(void) 18 | { 19 | double epsilon; 20 | double temp; 21 | 22 | epsilon = 1.0; 23 | temp = epsilon + 1.0; 24 | 25 | while (temp > 1.0) 26 | { 27 | epsilon = epsilon / 2.0; 28 | temp = epsilon + 1.0; 29 | } 30 | return (epsilon * 2.0); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /pypropep/cpropep/libnum/src/lu.c: -------------------------------------------------------------------------------- 1 | /* lu.c - PA = LU factorisation with pivoting 2 | * $Id: lu.c,v 1.4 2001/02/22 19:47:37 antoine Exp $ 3 | * Copyright (C) 2000 4 | * Antoine Lefebvre <antoine.lefebvre@polymtl.ca> 5 | * 6 | * 7 | * Licensed under the GPLv2 8 | */ 9 | 10 | #include <stdlib.h> 11 | #include <stdio.h> 12 | #include <math.h> 13 | #include "num.h" 14 | 15 | /* 16 | 17 | This algorithm will compute an LU factorisation on the augmented 18 | matrix (A) passed in arguments. 19 | 20 | This algorithm assumed the element on the diagonal of the lower 21 | triangular matrix set to 1. 22 | 23 | In order to save memory space, every coefficient of both matrix 24 | L and U are written in the original matrix overwriting initial 25 | values of A. 26 | 27 | */ 28 | 29 | int NUM_lu(double *matrix, double *solution, int neq) 30 | { 31 | int i, j, k; 32 | 33 | int idx; /* index of the larger pivot */ 34 | double big; /* the larger pivot found */ 35 | double tmp = 0.0; 36 | 37 | int *P; /* keep memory of permutation (column permutation) */ 38 | double *y; 39 | 40 | P = (int *) calloc (neq, sizeof(int)); 41 | y = (double *) calloc (neq, sizeof(double)); 42 | 43 | for (i = 0; i < neq; i++) 44 | { 45 | solution[i] = 0; /* reset the solution vector */ 46 | P[i] = i; /* initialize permutation vector */ 47 | } 48 | 49 | /* LU Factorisation */ 50 | 51 | for (i = 0; i < neq - 1; i++) /* line */ 52 | { 53 | for (j = i; j < neq; j++) /* column */ 54 | { 55 | tmp = 0.0; 56 | for (k = 0; k < i; k++) 57 | tmp += matrix[i + neq*P[k]] * matrix[k + neq*P[j]]; 58 | 59 | matrix[i + neq*P[j]] = matrix[i + neq*P[j]] - tmp; 60 | } 61 | 62 | /* find the larger pivot and interchange the columns */ 63 | big = 0.0; 64 | idx = i; 65 | for (j = i; j < neq; j++) 66 | { 67 | if (big < fabs(matrix[i + neq*P[j]])) /* we found a larger pivot */ 68 | { 69 | idx = j; 70 | big = fabs(matrix[i + neq*P[j]]); 71 | } 72 | } 73 | /* check if we have to interchange the lines */ 74 | if (idx != i) 75 | { 76 | tmp = P[i]; 77 | P[i] = P[idx]; 78 | P[idx] = tmp; 79 | } 80 | 81 | if (matrix[i + neq*P[i]] == 0.0) 82 | { 83 | printf("LU: matrix is singular, no unique solution.\n"); 84 | return NO_SOLUTION; 85 | } 86 | 87 | for (j = i+1; j < neq; j++) 88 | { 89 | tmp = 0.0; 90 | for (k = 0; k < i; k++) 91 | tmp += matrix[j + neq*P[k]] * matrix[k + neq*P[i]]; 92 | 93 | matrix[j + neq*P[i]] = (matrix[j + neq*P[i]] - tmp)/matrix[i + neq*P[i]]; 94 | } 95 | } 96 | 97 | i = neq - 1; 98 | tmp = 0.0; 99 | for (k = 0; k < neq-1; k++) 100 | tmp += matrix[i + neq*P[k]] * matrix[k + neq*P[i]]; 101 | 102 | matrix[i + neq*P[i]] = matrix[i + neq*P[i]] - tmp; 103 | 104 | 105 | /* End LU-Factorisation */ 106 | 107 | /* substitution for y Ly = b*/ 108 | for (i = 0; i < neq; i++) 109 | { 110 | tmp = 0.0; 111 | for (j = 0; j < i; j++) 112 | tmp += matrix[i + neq*P[j]] * y[j]; 113 | 114 | y[i] = matrix[i + neq*neq] - tmp; 115 | } 116 | 117 | /* substitution for x Ux = y*/ 118 | for (i = neq - 1; i >=0; i--) 119 | { 120 | if (matrix[i + neq*P[i]] == 0.0) 121 | { 122 | printf("LU: No unique solution exist.\n"); 123 | return NO_SOLUTION; 124 | } 125 | 126 | tmp = 0.0; 127 | for (j = i; j < neq; j++) 128 | tmp += matrix[i + neq*P[j]] * solution[P[j]]; 129 | 130 | solution[P[i]] = (y[i] - tmp)/matrix[i + neq*P[i]]; 131 | } 132 | 133 | free (y); 134 | return 0; 135 | } 136 | 137 | /* This function will divide each row of the matrix 138 | * by the highest element of this row. 139 | * This kind of scaling could improve precision while 140 | * solving some difficult matrix. 141 | */ 142 | int NUM_matscale(double *matrix, int neq) 143 | { 144 | int i; /* line */ 145 | int j; /* column */ 146 | 147 | double val; 148 | double tmp; 149 | 150 | for (i = 0; i < neq; i++) 151 | { 152 | val = 0; 153 | /* find the highest value */ 154 | for (j = 0; j < neq; j++) 155 | { 156 | tmp = fabs(matrix[i + neq*j]); 157 | val = (tmp > val) ? tmp : val; 158 | } 159 | 160 | /* divide element of the line by this value 161 | * including the right side 162 | * if the max value is defferent than zero 163 | */ 164 | if (val != 0.0) 165 | { 166 | for (j = 0; j < neq+1; j++) 167 | matrix[i + neq*j] = matrix[i + neq*j]/val; 168 | } 169 | } 170 | return 0; 171 | 172 | } 173 | -------------------------------------------------------------------------------- /pypropep/cpropep/libnum/src/newton.c: -------------------------------------------------------------------------------- 1 | #include <math.h> 2 | #include <stdio.h> 3 | 4 | #include "num.h" 5 | 6 | int NUM_newton(double (*f)(double x), double (*df)(double x), double x0, 7 | int nmax, double eps, double *ans) 8 | { 9 | 10 | int i = 0; 11 | double x1; 12 | double delta; 13 | 14 | do 15 | { 16 | if (i >= nmax) 17 | { 18 | return NO_CONVERGENCE; 19 | } 20 | 21 | x1 = x0 - f(x0)/df(x0); 22 | delta = fabs(x1 - x0)/fabs(x1); 23 | 24 | x0 = x1; 25 | i++; 26 | 27 | } while (delta > eps); 28 | 29 | *ans = x1; 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /pypropep/cpropep/libnum/src/print.c: -------------------------------------------------------------------------------- 1 | /* print.c 2 | * $Id: print.c,v 1.3 2000/10/20 20:17:20 antoine Exp $ 3 | * Copyright (C) 2000 4 | * Antoine Lefebvre <antoine.lefebvre@polymtl.ca> 5 | * 6 | * Licensed under the GPL 7 | */ 8 | 9 | #include <stdio.h> 10 | #include "num.h" 11 | 12 | int NUM_print_square_matrix(double *matrix, int neq) 13 | { 14 | int i = 0; 15 | int j = 0; 16 | 17 | for (i = 0; i < neq; i++) 18 | { 19 | for (j = 0; j < neq; j++) 20 | printf("% .5f ", matrix[i + neq*j]); 21 | printf("\n"); 22 | } 23 | printf("\n"); 24 | return 0; 25 | } 26 | 27 | int NUM_print_matrix(double *matrix, int neq) 28 | { 29 | int i = 0; 30 | int j = 0; 31 | 32 | for (i = 0; i < neq; i++) 33 | { 34 | for (j = 0; j <= neq; j++) 35 | printf("% .5e ", matrix[i + neq*j]); 36 | printf("\n"); 37 | } 38 | printf("\n"); 39 | return 0; 40 | } 41 | 42 | int NUM_print_vec(double *vec, int neq) 43 | { 44 | int i; 45 | for (i = 0; i < neq; i++) 46 | printf("% .5e ", vec[i]); 47 | printf("\n"); 48 | return 0; 49 | } 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /pypropep/cpropep/libnum/src/ptfix.c: -------------------------------------------------------------------------------- 1 | #include <math.h> 2 | #include <stdio.h> 3 | 4 | #include "num.h" 5 | 6 | int NUM_ptfix(double (*f)(double x), double x0, double nmax, 7 | double epsilon, double *ans) 8 | { 9 | 10 | int i = 0; 11 | double x1; 12 | double delta; 13 | 14 | do 15 | { 16 | if (i >= nmax) 17 | { 18 | return NO_CONVERGENCE; 19 | } 20 | 21 | x1 = f(x0); 22 | 23 | delta = fabs(x1 - x0)/fabs(x1); 24 | x0 = x1; 25 | i++; 26 | 27 | } while (delta > epsilon); 28 | 29 | *ans = x1; 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /pypropep/cpropep/libnum/src/rk4.c: -------------------------------------------------------------------------------- 1 | #include <stdlib.h> 2 | #include <math.h> 3 | #include "num.h" 4 | 5 | 6 | int NUM_rk4(int (*f)(int neq, double time, double *y, double *dy, void *data), 7 | int neq, double step, double duration, double *ic, 8 | double **y, void *data) 9 | { 10 | int i; 11 | int n; 12 | 13 | int status; 14 | 15 | int length; 16 | 17 | double t = 0.0; 18 | 19 | double *tmp; 20 | double *dy; 21 | double *K1, *K2, *K3, *K4; 22 | 23 | double *a; 24 | 25 | tmp = (double *) malloc(sizeof(double) * neq); 26 | dy = (double *) malloc(sizeof(double) * neq); 27 | 28 | K1 = (double *) malloc(sizeof(double) * neq); 29 | K2 = (double *) malloc(sizeof(double) * neq); 30 | K3 = (double *) malloc(sizeof(double) * neq); 31 | K4 = (double *) malloc(sizeof(double) * neq); 32 | 33 | /* allocation of the answer vector */ 34 | length = (int)ceil(duration/step) + 1; 35 | a = *y = (double *) malloc(sizeof(double) * neq * length); 36 | 37 | for (i = 0; i < neq; i++) 38 | { 39 | tmp[i] = a[i + neq*0] = ic[i]; /* initials conditions */ 40 | } 41 | 42 | for (n = 0; n < Round(duration/step); n++) 43 | { 44 | 45 | for (i = 0; i < neq; i++) 46 | { 47 | status = f(neq, t, tmp, dy, data); 48 | 49 | if (status) 50 | return status; 51 | 52 | K1[i] = step * dy[i]; 53 | 54 | tmp[i] = a[i + neq*n] + K1[i]/2; /* for the next step */ 55 | } 56 | 57 | /* FIXME: verify the coefficient t + step/2 */ 58 | for (i = 0; i < neq; i++) 59 | { 60 | status = f(neq, t + step/2, tmp, dy, data); 61 | 62 | if (status) 63 | return -1; 64 | 65 | K2[i] = step * dy[i]; 66 | 67 | tmp[i] = a[i + neq*n] + K2[i]/2; 68 | } 69 | 70 | for (i = 0; i < neq; i++) 71 | { 72 | status = f(neq, t + step/2, tmp, dy, data); 73 | 74 | if (status) 75 | return status; 76 | 77 | K3[i] = step * dy[i]; 78 | 79 | tmp[i] = a[i + neq*n] + K3[i]; 80 | } 81 | 82 | for (i = 0; i < neq; i++) 83 | { 84 | status = f(neq, t + step, tmp, dy, data); 85 | if (status) 86 | return -1; 87 | 88 | K4[i] = step * dy[i]; 89 | } 90 | 91 | for (i = 0; i < neq; i++) 92 | a[i + neq*(n+1)] = a[i + neq*n] + 93 | (1.0/6.0)*(K1[i] + 2.0*K2[i] + 2.0*K3[i] + K4[i]); 94 | 95 | t = t + step; 96 | } 97 | 98 | free(tmp); 99 | free(dy); 100 | free(K1); 101 | free(K2); 102 | free(K3); 103 | free(K4); 104 | 105 | return length; 106 | } 107 | 108 | -------------------------------------------------------------------------------- /pypropep/cpropep/libnum/src/rkf.c: -------------------------------------------------------------------------------- 1 | #include <stdlib.h> 2 | #include <math.h> 3 | #include <float.h> 4 | 5 | #include "num.h" 6 | 7 | /* neq: number of equations 8 | * step: initial time step 9 | * duration: total duration of the simulation 10 | * ic: initial conditions vector 11 | * y: solution vector 12 | * epsil: precision on the data 13 | * data: pointer to data structure 14 | */ 15 | 16 | /* Runge-Kutta-Fehlberg 4-5 order */ 17 | 18 | int NUM_rkf(int (*f)(int neq, double time, double *y, double *dy, void *data), 19 | int neq, double step, double duration, double *ic, 20 | double **y, double epsil, void *data) 21 | { 22 | int i; 23 | int n; 24 | 25 | int col; 26 | 27 | double h; 28 | double t = 0.0; 29 | 30 | double *a; 31 | 32 | double *tmp; 33 | double *dy; 34 | double *K1, *K2, *K3, *K4, *K5, *K6; 35 | 36 | double *E; /* Error vector of the error */ 37 | 38 | double err; /* maximum error */ 39 | double beta; 40 | 41 | tmp = (double *) malloc(sizeof(double) * neq); 42 | dy = (double *) malloc(sizeof(double) * neq); 43 | 44 | E = (double *) malloc(sizeof(double) * neq); 45 | 46 | K1 = (double *) malloc(sizeof(double) * neq); 47 | K2 = (double *) malloc(sizeof(double) * neq); 48 | K3 = (double *) malloc(sizeof(double) * neq); 49 | K4 = (double *) malloc(sizeof(double) * neq); 50 | K5 = (double *) malloc(sizeof(double) * neq); 51 | K6 = (double *) malloc(sizeof(double) * neq); 52 | 53 | 54 | h = step; 55 | n = 0; 56 | 57 | col = neq + 1; 58 | 59 | a = *y = (double *) malloc(sizeof(double) * col * (n + 1)); 60 | 61 | 62 | for (i = 0; i < neq; i++) 63 | { 64 | tmp[i] = a[i + col*0] = ic[i]; /* initial conditions */ 65 | } 66 | 67 | while (t < duration) 68 | { 69 | a = *y = (double *) realloc(*y, sizeof(double) * col * (n + 2)); 70 | 71 | for (i = 0; i < neq; i++) 72 | { 73 | f(neq, t, tmp, dy, data); 74 | K1[i] = h * dy[i]; 75 | 76 | tmp[i] = a[i + col*n] + K1[i]/4.0; /* for the next step */ 77 | } 78 | 79 | for (i = 0; i < neq; i++) 80 | { 81 | f(neq, t + h/4.0, tmp, dy, data); 82 | K2[i] = h * dy[i]; 83 | 84 | tmp[i] = a[i + col*n] + 3.0*K1[i]/32.0 + 9.0*K2[i]/32.0; 85 | 86 | } 87 | 88 | for (i = 0; i < neq; i++) 89 | { 90 | f(neq, t + 3.0*h/8.0, tmp, dy, data); 91 | K3[i] = h * dy[i]; 92 | 93 | tmp[i] = a[i + col*n] + 1932.0*K1[i]/2197.0 - 7200.0*K2[i]/2197.0 + 94 | 7296.0*K3[i]/2197.0; 95 | } 96 | 97 | 98 | for (i = 0; i < neq; i++) 99 | { 100 | f(neq, t + 12.0*h/13.0, tmp, dy, data); 101 | K4[i] = h * dy[i]; 102 | 103 | tmp[i] = a[i + col*n] + 439.0*K1[i]/216.0 - 8.0*K2[i] + 104 | 3680.0*K3[i]/513.0 - 845.0*K4[i]/4104.0; 105 | } 106 | 107 | for (i = 0; i < neq; i++) 108 | { 109 | f(neq, t + h, tmp, dy, data); 110 | K5[i] = h * dy[i]; 111 | 112 | tmp[i] = a[i + col*n] - 8.0*K1[i]/27.0 + 2.0*K2[i] - 113 | 3544.0*K3[i]/2565.0 + 1859.0*K4[i]/4104.0 - 11.0*K5[i]/40.0; 114 | } 115 | 116 | for (i = 0; i < neq; i++) 117 | { 118 | f(neq, t + h/2.0, tmp, dy, data); 119 | K6[i] = h * dy[i]; 120 | } 121 | 122 | err = 0.0; 123 | for (i = 0; i < neq; i++) 124 | { 125 | E[i] = fabs(K1[i]/360.0 - 128.0*K3[i]/4275.0 - 2197.0*K4[i]/75240.0 + 126 | K5[i]/50.0 + 2.0*K6[i]/55.0); /* /h ?? */ 127 | //printf("E[%d] = %f\n", i, E[i]); 128 | err = ((E[i] > err) ? E[i] : err); 129 | } 130 | 131 | //printf("err = %e\n", err); 132 | 133 | if ((err < epsil) || (h <= step/1000) ) 134 | { 135 | t += h; 136 | 137 | for (i = 0; i < neq; i++) 138 | { 139 | a[i + col*(n+1)] = a[i + col*n] + 25.0*K1[i]/216.0 + 140 | 1408.0*K3[i]/2565.0 + 2197.0*K4[i]/4104.0 - 0.2*K5[i]; 141 | } 142 | /* store the time */ 143 | a[neq + col*(n+1)] = t; 144 | n++; 145 | } 146 | 147 | beta = pow(epsil/(2*err), 0.25); 148 | 149 | if (beta < 0.1) 150 | { 151 | h = 0.1* h; 152 | } 153 | else if (beta > 4) 154 | { 155 | h = 4.0*h; 156 | } 157 | else 158 | { 159 | h = beta * h; 160 | } 161 | 162 | /* we have to prevent too little h*/ 163 | if (h < step/1000) 164 | h = step/1000; 165 | 166 | /* prevent too big */ 167 | if (h > duration/16) 168 | h = duration/16; 169 | 170 | if (t + h > duration) 171 | { 172 | h = duration - t; 173 | } 174 | 175 | } 176 | 177 | return n+1; 178 | } 179 | 180 | -------------------------------------------------------------------------------- /pypropep/cpropep/libnum/src/sec.c: -------------------------------------------------------------------------------- 1 | #include <math.h> 2 | #include <stdio.h> 3 | 4 | #include "num.h" 5 | 6 | /* Resolution of non-linear equation of the for 7 | f(x) = 0 with the secante method which is a modified 8 | newton method using secante instead of derivative */ 9 | int NUM_sec(double (*f)(double x), double x0, double x1, int nmax, 10 | double epsilon, double *ans) 11 | { 12 | int i = 0; 13 | double x2; 14 | double delta; 15 | 16 | do 17 | { 18 | if (i >= nmax) 19 | { 20 | return NO_CONVERGENCE; 21 | } 22 | 23 | x2 = x1 - f(x1)*(x1 - x0)/(f(x1) - f(x0)); 24 | 25 | delta = fabs(x2 - x1)/fabs(x2); 26 | x0 = x1; 27 | x1 = x2; 28 | i++; 29 | } while (delta > epsilon); 30 | 31 | *ans = x2; 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /pypropep/cpropep/libnum/src/simpson.c: -------------------------------------------------------------------------------- 1 | 2 | #include <math.h> 3 | 4 | int simpson(double *data, int n_point, int col, int off, double *integral) 5 | { 6 | 7 | int i; 8 | 9 | double val = 0; 10 | 11 | double x0, x1, x2, fx0, fx1, fx2; 12 | 13 | double dd_x0x1; 14 | double dd_x1x2; 15 | double dd_x0x1x2; 16 | 17 | int beg = 0; 18 | if ((n_point%2) != 1) 19 | { 20 | beg = 1; 21 | x0 = data[0 + 0*col]; 22 | x1 = data[0 + 1*col]; 23 | fx0 = data[off + 0*col]; 24 | fx1 = data[off + 1*col]; 25 | val += (fx0+fx1)*(x1-x0)/2; 26 | } 27 | for (i = beg; i < n_point - 2; i += 2) 28 | { 29 | x0 = data[0 + (i+0)*col]; 30 | x1 = data[0 + (i+1)*col]; 31 | x2 = data[0 + (i+2)*col]; 32 | 33 | fx0 = data[off + (i+0)*col]; 34 | fx1 = data[off + (i+1)*col]; 35 | fx2 = data[off + (i+2)*col]; 36 | 37 | dd_x0x1 = (fx1 - fx0)/(x1 - x0); 38 | dd_x1x2 = (fx2 - fx1)/(x2 - x1); 39 | 40 | dd_x0x1x2 = (dd_x1x2 - dd_x0x1)/(x2 - x0); 41 | 42 | val += (fx0 - dd_x0x1*x0 + dd_x0x1x2*x0*x1)*(x2 - x0) + 43 | (dd_x0x1 - dd_x0x1x2*(x0+x1))*(pow(x2, 2)-pow(x0, 2))/2 + 44 | dd_x0x1x2*(pow(x2, 3) - pow(x0,3))/3; 45 | } 46 | 47 | *integral = val; 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /pypropep/cpropep/libnum/src/spline.c: -------------------------------------------------------------------------------- 1 | /* spline.c - Cubic spline interpolation 2 | * $Id: spline.c,v 1.1 2001/06/10 21:06:00 antoine Exp $ 3 | * Copyright (C) 2000 4 | * Antoine Lefebvre <antoine.lefebvre@polymtl.ca> 5 | * 6 | * 7 | * Licensed under the GPLv2 8 | */ 9 | 10 | 11 | #include <stdlib.h> 12 | #include <stdio.h> 13 | #include <math.h> 14 | #include "num.h" 15 | 16 | #define OUT_OF_RANGE -1 17 | 18 | /* Computation of natural spline. 19 | 20 | */ 21 | 22 | int create_spline(double *data, int n_point, double *spline) 23 | { 24 | int i; 25 | int col; 26 | double hi0, hi1, hi2; 27 | double fi0, fi1, fi2; 28 | double *matrix; 29 | 30 | col = 2; 31 | 32 | matrix = (double *) calloc (n_point*(n_point+1), sizeof(double)); 33 | 34 | /* create the linear system */ 35 | matrix[0 + 0] = 1; 36 | 37 | for (i = 1; i < n_point - 1; i++) 38 | { 39 | hi0 = data[0 + (i+0)*col] - data[0 + (i-1)*col]; 40 | hi1 = data[0 + (i+1)*col] - data[0 + (i-0)*col]; 41 | hi2 = data[0 + (i+1)*col] - data[0 + (i-1)*col]; 42 | 43 | fi0 = data[1 + (i-1)*col]; /* f(x_(i-1)) */ 44 | fi1 = data[1 + (i-0)*col]; /* f(x_i) */ 45 | fi2 = data[1 + (i+1)*col]; /* f(x_(i+1)) */ 46 | 47 | matrix[i + (i-1)*n_point] = hi0/(hi0+hi1); 48 | matrix[i + (i-0)*n_point] = 2; 49 | matrix[i + (i+1)*n_point] = hi1/(hi0+hi1); 50 | 51 | matrix[i + n_point*n_point] = 6*( (fi2-fi1)/hi1 - (fi1-fi0)/hi0)/hi2; 52 | } 53 | 54 | matrix[n_point - 1 + (n_point - 1)*n_point] = 1; 55 | 56 | NUM_print_matrix(matrix, n_point); 57 | NUM_lu(matrix, spline, n_point); 58 | NUM_print_vec(spline, n_point); 59 | 60 | free(matrix); 61 | return 0; 62 | } 63 | 64 | 65 | 66 | int eval_spline(double *data, double *spline, int n_point, double x, double *y) 67 | { 68 | 69 | int i; 70 | 71 | int col = 2; 72 | 73 | double hi0; 74 | double xi0; 75 | double xi1; 76 | double fi0; 77 | double fi1; 78 | double d2f0; 79 | double d2f1; 80 | 81 | /* we must find the interval in which x is located */ 82 | 83 | i = 0; 84 | 85 | if ((x < data[0]) || (x > data[0 + (n_point - 1)*col])) 86 | { 87 | printf("Out of range: %f\n", x); 88 | return OUT_OF_RANGE; 89 | } 90 | 91 | for (i = 0; x > data[0 + i*col]; i++); 92 | 93 | xi0 = data[0 + (i-1)*col]; 94 | xi1 = data[0 + i*col]; 95 | hi0 = xi1 - xi0; 96 | fi0 = data[1 + (i-1)*col]; 97 | fi1 = data[1 + i*col]; 98 | d2f0 = spline[i-1]; 99 | d2f1 = spline[i]; 100 | 101 | /* evaluation of the spline at the specified point */ 102 | *y = -d2f0*pow(x-xi1, 3)/(6*hi0) + d2f1*pow(x-xi0, 3)/(6*hi0) 103 | -(fi0/hi0 - hi0*d2f0/6)*(x-xi1) + (fi1/hi0 - hi0*d2f1/6)*(x-xi0); 104 | 105 | return 0; 106 | } 107 | 108 | 109 | -------------------------------------------------------------------------------- /pypropep/cpropep/libnum/src/sysnewton.c: -------------------------------------------------------------------------------- 1 | /* sysnewton.c - Solve system of non-linear equations with newton's method 2 | * $Id: sysnewton.c,v 1.1 2000/10/20 20:17:20 antoine Exp $ 3 | * Copyright (C) 2000 4 | * Antoine Lefebvre <antoine.lefebvre@polymtl.ca> 5 | * 6 | * Licensed under the GPL 7 | */ 8 | 9 | 10 | #include <stdlib.h> 11 | #include <stdio.h> 12 | #include <math.h> 13 | #include "num.h" 14 | 15 | 16 | double norme(double *x, int n); 17 | 18 | /* Jac : Pointer to a matrix of pointer to function (Jacobian matrix) 19 | * R : Pointer to a vector of pointer to function (Residue vector) 20 | * x : Initial estimate of the solution (will be overwrite by the answer) 21 | * nvar: Number of variable in the system 22 | * nmax: Maximal number of iterations 23 | * eps : Precision on the answer 24 | */ 25 | int NUM_sysnewton(func_t *Jac, func_t *R, double *x, int nvar, 26 | int nmax, double eps) 27 | { 28 | int i, j, l; 29 | 30 | double *r; 31 | double *dx; /* solution of the system */ 32 | double *matrix; /* the matrix to be solve */ 33 | 34 | /* Allow space for the left hand side */ 35 | matrix = (double *) malloc (sizeof(double) * nvar * (nvar + 1)); 36 | 37 | r = (double *) malloc (sizeof(double) * nvar); 38 | dx = (double *) malloc(sizeof(double) * nvar); 39 | 40 | l = 0; 41 | do 42 | { 43 | /* set up the matrix */ 44 | for (i = 0; i < nvar; i++) /* line */ 45 | { 46 | for (j = 0; j < nvar; j++) /* column */ 47 | { 48 | matrix[i + nvar*j] = Jac[i + nvar*j](x); 49 | } 50 | r[i] = matrix[i + nvar*nvar] = -R[i](x); 51 | } 52 | 53 | /* if the matrix is singular */ 54 | if (NUM_lu(matrix, dx, nvar)) 55 | return NO_CONVERGENCE; 56 | 57 | for (i = 0; i < nvar; i++) 58 | { 59 | x[i] = x[i] + dx[i]; 60 | } 61 | 62 | if ((norme(dx, nvar)/norme(x, nvar) < eps) && 63 | (norme(r, nvar) <= eps)) 64 | { 65 | /* the solution converged */ 66 | return 0; 67 | } 68 | 69 | l++; 70 | } while (l < nmax); 71 | 72 | return NO_CONVERGENCE; 73 | 74 | } 75 | 76 | 77 | double norme(double *x, int n) 78 | { 79 | int i; 80 | double a = 0.0; 81 | for (i = 0; i < n; i++) 82 | { 83 | a += pow(x[i], 2); 84 | } 85 | return sqrt(a); 86 | } 87 | -------------------------------------------------------------------------------- /pypropep/cpropep/libnum/src/test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonnydyer/pypropep/ed94c4518689031ca035212c13d7a88c008b5860/pypropep/cpropep/libnum/src/test -------------------------------------------------------------------------------- /pypropep/cpropep/libnum/src/test.c: -------------------------------------------------------------------------------- 1 | /* test.c - Testing the functionnality of the various method 2 | * $Id: test.c,v 1.5 2001/06/10 21:06:00 antoine Exp $ 3 | * Copyright (C) 2000 4 | * Antoine Lefebvre <antoine.lefebvre@polymtl.ca> 5 | * 6 | * Licensed under the GPL 7 | */ 8 | 9 | #include <stdio.h> 10 | #include <stdlib.h> 11 | #include <math.h> 12 | 13 | //#include <time.h> 14 | 15 | #include "num.h" 16 | 17 | FILE * errorfile; 18 | FILE * outputfile; 19 | 20 | int test_rk4(void); 21 | int test_lu(void); 22 | int test_sysnewton(void); 23 | int test_sec(void); 24 | int test_newton(void); 25 | int test_ptfix(void); 26 | int test_spline(void); 27 | 28 | /* g1(x) = x + 1 - ln(x) */ 29 | double g1(double x) { 30 | return x + 1 - log(x); 31 | } 32 | /* f1(x) = ln(x) - 1 */ 33 | double f1(double x) { 34 | return log(x) - 1; 35 | } 36 | /* f1'(x) = 1/x */ 37 | double df1(double x) { 38 | return 1/x; 39 | } 40 | double dg1(double x) { 41 | return 1 - 1/x; 42 | } 43 | /* f(x) = (x-1)^3 */ 44 | double f(double x) { 45 | return pow(x-1, 3); 46 | } 47 | 48 | int function(int neq, double time, double *y, double *dy, 49 | void *data) 50 | { 51 | 52 | dy[0] = y[1]; 53 | dy[1] = -9.8; 54 | dy[2] = y[3]; 55 | dy[3] = 0; 56 | return 0; 57 | } 58 | 59 | /* functions to test the sysnewton algorythm 60 | * 61 | * The system to be solve is the following 62 | * r1(x1, x2) = e^x1 - x2 = 0 63 | * r2(x1, x2) = x1^2 + x2^2 - 16 = 0 64 | * 65 | */ 66 | double r1(double *x) 67 | { 68 | return exp(x[0]) - x[1]; 69 | } 70 | double r2(double *x) 71 | { 72 | return pow(x[0], 2) + pow(x[1], 2) - 16; 73 | } 74 | /* We need partial derivative of these function with each variable 75 | * dr1_dx1(x1, x2) = e^x1 76 | * dr1_dx2(x1, x2) = -1 77 | * dr2_dx1(x1, x2) = 2*x1 78 | * dr2_dx2(x1, x2) = 2*x2 79 | */ 80 | double dr1_dx1(double *x) 81 | { 82 | return exp(x[0]); 83 | } 84 | double dr1_dx2(double *x) 85 | { 86 | return -1; 87 | } 88 | double dr2_dx1(double *x) 89 | { 90 | return 2*x[0]; 91 | } 92 | double dr2_dx2(double *x) 93 | { 94 | return 2*x[1]; 95 | } 96 | 97 | 98 | int main(void) 99 | { 100 | 101 | 102 | test_lu(); 103 | test_spline(); 104 | 105 | test_rk4(); 106 | test_sysnewton(); 107 | 108 | test_sec(); 109 | test_newton(); 110 | test_ptfix(); 111 | 112 | return 0; 113 | } 114 | 115 | int test_sec(void) 116 | { 117 | double ans; 118 | printf("Testing Secante method\n"); 119 | if (NUM_sec(f1, 1, 2, 100, 0.0001, &ans)) 120 | printf("No solution: error in the method.\n\n"); 121 | else 122 | printf("Solution: %f\n\n", ans); 123 | return 0; 124 | } 125 | 126 | int test_newton(void) 127 | { 128 | double ans; 129 | printf("Testing Newton method\n"); 130 | if (NUM_newton(f1, df1, 1, 100, 0.0001, &ans)) 131 | printf("No solution: error in the method.\n\n"); 132 | else 133 | printf("Solution: %f\n\n", ans); 134 | return 0; 135 | } 136 | 137 | int test_ptfix(void) 138 | { 139 | double ans; 140 | printf("Testing fixed point method\n"); 141 | if (NUM_ptfix(g1, 1, 100, 0.0001, &ans)) 142 | printf("No solution: error in the method.\n"); 143 | else 144 | printf("Solution: %f\n\n", ans); 145 | return 0; 146 | } 147 | 148 | int test_sysnewton(void) 149 | { 150 | func_t *jac; 151 | func_t *r; 152 | int nvar = 2; 153 | 154 | double x[2]; /* solution vector, contain initially the initial conditions */ 155 | 156 | printf("Testing newton method for solving" 157 | " non-linear system of equations.\n"); 158 | 159 | jac = (func_t *) malloc (sizeof(func_t) * nvar * nvar); 160 | r = (func_t *) malloc (sizeof(func_t) * nvar); 161 | 162 | /* Initialize the functions pointers array */ 163 | r[0] = r1; 164 | r[1] = r2; 165 | 166 | jac[0] = dr1_dx1; 167 | jac[1] = dr2_dx1; 168 | jac[2] = dr1_dx2; 169 | jac[3] = dr2_dx2; 170 | 171 | /* set the initial estimate */ 172 | x[0] = 2.8; 173 | x[1] = 2.8; 174 | 175 | /* call the sysnewton function */ 176 | 177 | NUM_sysnewton(jac, r, x, 2, 100, 1e-8); 178 | 179 | /* print the solution */ 180 | 181 | printf("Solution: x1 = %f, x2 = %f\n", x[0], x[1]); 182 | 183 | printf("\n"); 184 | return 0; 185 | 186 | } 187 | 188 | int test_lu(void) 189 | { 190 | int i; 191 | double *matrix; 192 | double *solution; 193 | int size = 8; 194 | 195 | printf("Testing the LU factorisation algotythm.\n"); 196 | matrix = (double *) malloc (sizeof(double)*size*(size+1)); 197 | solution = (double *) malloc (sizeof(double)*size); 198 | 199 | matrix[0] = 4.77088e-02; matrix[8] = 1.17204e-01; matrix[16] = 1.88670e-02; 200 | matrix[1] = 1.17204e-01; matrix[9] = 4.07815e-01; matrix[17] = 1.25752e-02; 201 | matrix[2] = 1.88670e-02; matrix[10] = 1.25752e-02; matrix[18] = 4.40215e-02; 202 | matrix[3] = 0.00000e+00; matrix[11] = 0.00000e+00; matrix[19] = 0.00000e+00; 203 | matrix[4] = 0.00000e+00; matrix[12] = 0.00000e+00; matrix[20] = 0.00000e+00; 204 | matrix[5] = 1.00000e+00; matrix[13] = 0.00000e+00; matrix[21] = 3.00000e+00; 205 | matrix[6] = 2.97603e-02; matrix[14] = 8.67031e-02; matrix[22] = 2.51546e-02; 206 | matrix[7] = 5.15962e+01; matrix[15] = 1.67940e+02; matrix[23] = -7.46975e+01; 207 | 208 | matrix[24] = 0.00000e+00; 209 | matrix[25] = 0.00000e+00; 210 | matrix[26] = 0.00000e+00; 211 | matrix[27] = 1.28681e-02; 212 | matrix[28] = 0.00000e+00; 213 | matrix[29] = 0.00000e+00; 214 | matrix[30] = 6.43406e-03; 215 | matrix[31] = -7.23674e-01; 216 | 217 | matrix[32] = 0.0000e+00; matrix[40] = 1.00000e+00; matrix[48] = 2.97603e-02; 218 | matrix[33] = 0.0000e+00; matrix[41] = 0.00000e+00; matrix[49] = 8.67031e-02; 219 | matrix[34] = 0.0000e+00; matrix[42] = 3.00000e+00; matrix[50] = 2.51546e-02; 220 | matrix[35] = 0.0000e+00; matrix[43] = 0.00000e+00; matrix[51] = 6.43406e-03; 221 | matrix[36] = 0.0000e+00; matrix[44] = 2.00000e+00; matrix[52] = 0.00000e+00; 222 | matrix[37] = 2.0000e+00; matrix[45] = 0.00000e+00; matrix[53] = 0.00000e+00; 223 | matrix[38] = 0.0000e+00; matrix[46] = 0.00000e+00; matrix[54] = 1.45616e-02; 224 | matrix[39] = 0.0000e+00; matrix[47] =-9.68727e+03; matrix[55] =-3.06747e+01; 225 | 226 | matrix[56] = 5.15962e+01; matrix[64] =-1.12677e+01; 227 | matrix[57] = 1.67940e+02; matrix[65] = 8.29437e+00; 228 | matrix[58] =-7.46975e+01; matrix[66] =-7.39145e+01; 229 | matrix[59] =-7.23674e-01; matrix[67] =-5.99249e-01; 230 | matrix[60] = 0.00000e+00; matrix[68] = 1.58804e-02; 231 | matrix[61] =-9.68727e+03; matrix[69] =-9.62706e+03; 232 | matrix[62] =-3.06747e+01; matrix[70] =-4.63904e+01; 233 | matrix[63] = 4.68590e+05; matrix[71] = 2.37298e+05; 234 | 235 | //NUM_matscale(matrix, size); 236 | NUM_print_matrix(matrix, size); 237 | 238 | if (NUM_lu(matrix, solution, size)) 239 | printf("No solution: Error in the numerical method,\n"); 240 | else 241 | NUM_print_vec(solution, size); 242 | /* 243 | for (i = 0; i < size; i++) 244 | { 245 | if (solution[i] != 1.0) 246 | { 247 | printf("Error found in the solution.\n"); 248 | } 249 | } 250 | */ 251 | 252 | printf("\n"); 253 | return 0; 254 | } 255 | 256 | 257 | int test_rk4(void) 258 | { 259 | int i, n; 260 | double *ans; 261 | double *ic; 262 | 263 | printf("\nTesting the RK4 and RKF algorythm.\n"); 264 | 265 | ic = (double *) malloc(sizeof(double) * 4); 266 | 267 | ic[0] = 0; 268 | ic[1] = 100; 269 | ic[2] = 0; 270 | ic[3] = 10; 271 | 272 | /* it return the length of the answer vector */ 273 | //n = NUM_rk4 (function, 4, 0.1, 10, ic, &ans, NULL); 274 | 275 | //for (i = 0; i < n; i++) 276 | //{ 277 | // printf("%f %f %f %f \n", ans[4*i], ans[1 + 4*i], ans[2+4*i], ans[3+4*i]); 278 | //} 279 | 280 | n = NUM_rkf (function, 4, 0.1, 20, ic, &ans, 1e-4, NULL); 281 | 282 | printf("n = %i\n", n); 283 | 284 | for( i = 0; i < n; i++) 285 | { 286 | printf("%f %f %f %f %f\n", ans[4 + 5*i], ans[5*i], 287 | ans[1+5*i], ans[2 + 5*i], ans[3 + 5*i]); 288 | } 289 | 290 | free(ic); 291 | free(ans); 292 | 293 | return 0; 294 | } 295 | 296 | int test_spline(void) 297 | { 298 | int i; 299 | 300 | double *data; 301 | 302 | double *spline; 303 | 304 | int size = 10; 305 | 306 | double ans; 307 | 308 | printf("Testing the spline function.\n\n"); 309 | data = malloc (2 * size * sizeof(double)); 310 | spline = malloc (size * sizeof(double)); 311 | 312 | data[0] = 0; /* x0 */ 313 | data[1] = 55; /* y0 */ 314 | data[2] = 5; /* x1 */ 315 | data[3] = 60; /* y1 */ 316 | data[4] = 10; /* ... */ 317 | data[5] = 58; 318 | data[6] = 15; 319 | data[7] = 54; 320 | data[8] = 20; 321 | data[9] = 55; 322 | data[10] = 25; 323 | data[11] = 60; 324 | data[12] = 30; 325 | data[13] = 54; 326 | data[14] = 35; 327 | data[15] = 57; 328 | data[16] = 40; 329 | data[17] = 52; 330 | data[18] = 45; 331 | data[19] = 49; 332 | 333 | create_spline(data, size, spline); 334 | 335 | for (i = 0; i < 45; i++) 336 | { 337 | eval_spline(data, spline, size, (double)i, &ans); 338 | printf("%d %f\n", i, ans); 339 | } 340 | 341 | printf("Spline test finish\n"); 342 | return 0; 343 | } 344 | 345 | -------------------------------------------------------------------------------- /pypropep/cpropep/libnum/src/trapeze.c: -------------------------------------------------------------------------------- 1 | 2 | int trapeze(double *data, int n_point, int col, int off, double *integral) 3 | { 4 | int i; 5 | double val = 0.0; 6 | 7 | for (i = 0; i < n_point - 1; i++) 8 | { 9 | val += (data[off + i*col] + data[off + (i + 1)*col])* 10 | (data[0 + (i + 1)*col] - data[0 + i*col])/2; 11 | } 12 | 13 | *integral = val; 14 | 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /pypropep/cpropep/libthermo/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: 3 | @mkdir -p lib 4 | make -C src all 5 | 6 | clean: 7 | @rm -rf lib 8 | make -C src clean 9 | 10 | deep-clean: clean 11 | make -C src deep-clean 12 | -------------------------------------------------------------------------------- /pypropep/cpropep/libthermo/Makefile.win: -------------------------------------------------------------------------------- 1 | 2 | CC = bcc32 3 | CPP32 = cpp32 4 | LIBRARIAN = tlib 5 | LINKER = ilink32 6 | RC = brc32 7 | 8 | COPT = -3 -O2 -w-8004 -w-8012 -w-8057 -IC:\borland\bcc55\include 9 | LDOPT = -LC:\borland\bcc55\lib 10 | 11 | INCLUDEDIR = -I..\..\libnum\ -I. 12 | 13 | DEF = -DBORLAND 14 | 15 | COMPAT_LIBNAME = compat.lib 16 | CPROPEP_LIBNAME = cpropep.lib 17 | THERMO_LIBNAME = thermo.lib 18 | 19 | COMPAT_LIBOBJS = compat.obj getopt.obj 20 | THERMO_LIBOBJS = load.obj thermo.obj 21 | CPROPEP_LIBOBJS = equilibrium.obj print.obj performance.obj derivative.obj 22 | 23 | TLIBCOMPAT = +compat.obj +getopt.obj 24 | TLIBTHERMO = +load.obj +thermo.obj 25 | TLIBCPROPEP = +equilibrium.obj +print.obj +performance.obj +derivative.obj 26 | .SUFFIXES: .c 27 | 28 | all: $(CPROPEP_LIBNAME) $(THERMO_LIBNAME) $(COMPAT_LIBNAME) 29 | 30 | .c.obj: 31 | $(CC) $(DEF) $(INCLUDEDIR) $(COPT) -c $*.c -o $*.obj 32 | 33 | $(COMPAT_LIBNAME): $(COMPAT_LIBOBJS) 34 | tlib $@ $(TLIBCOMPAT) 35 | 36 | $(CPROPEP_LIBNAME): $(CPROPEP_LIBOBJS) 37 | tlib $@ $(TLIBCPROPEP) 38 | 39 | $(THERMO_LIBNAME): $(THERMO_LIBOBJS) 40 | tlib $@ $(TLIBTHERMO) 41 | 42 | clean: 43 | del *.obj 44 | del *.bak 45 | del *.tds 46 | 47 | deep-clean: clean 48 | del $(COMPAT_LIBNAME) 49 | del $(CPROPEP_LIBNAME) 50 | del $(THERMO_LIBNAME) 51 | -------------------------------------------------------------------------------- /pypropep/cpropep/libthermo/include/load.h: -------------------------------------------------------------------------------- 1 | #ifndef load_h 2 | #define load_h 3 | 4 | /*************************************************************** 5 | FUNCTION: Load the propellant data contain in filename 6 | 7 | PARAMETER: filename should be the path of the file containing 8 | the information on propellant. It should be exactly 9 | in the format of the file propellant.dat include 10 | in the distribution. 11 | 12 | COMMENTS: It load the information in the global variable 13 | propellant_list[MAX_PROPELLANT] that is of type 14 | propellant_t 15 | 16 | AUTHOR: Antoine Lefebvre 17 | modification bye Mark Pinese 18 | ****************************************************************/ 19 | int load_propellant(char *filename); 20 | 21 | /*************************************************************** 22 | FUNCTION: Load the thermo data contain in filename 23 | 24 | PARAMETER: filename should be the path of the file containing 25 | the thermo information . It should be exactly 26 | in the format of the file thermo.dat include 27 | in the distribution. 28 | 29 | COMMENTS: It load the information in the global variable 30 | thermo_list[MAX_THERMO] that is of type 31 | thermo_t 32 | 33 | AUTHOR: Antoine Lefebvre 34 | modification bye Mark Pinese 35 | ****************************************************************/ 36 | int load_thermo(char *filename); 37 | 38 | /*************************************************************** 39 | Removes trailing ' ' in str. If str is all ' ', removes all 40 | but the first. 41 | str - pointer to a character array (not necessarily a string) 42 | len - length of str. 43 | 44 | AUTHOR: Mark Pinese 45 | ****************************************************************/ 46 | void trim_spaces(char *str, unsigned int len); 47 | 48 | 49 | #endif 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /pypropep/cpropep/libthermo/include/thermo.h: -------------------------------------------------------------------------------- 1 | #ifndef thermo_h 2 | #define thermo_h 3 | 4 | #include "equilibrium.h" 5 | #include "const.h" 6 | 7 | /* MACRO: Number of symbol in the symbol table */ 8 | #define N_SYMB 102 9 | 10 | /*************************************************************** 11 | TYPE: Structure to hold information of species contain in the 12 | thermo data file 13 | ****************************************************************/ 14 | typedef struct _thermo 15 | { 16 | char name[19]; 17 | char comments[57]; 18 | int nint; /* number of different temperature interval */ 19 | char id[7]; /* identification code */ 20 | int elem[5]; 21 | int coef[5]; 22 | state_t state; 23 | double weight; /* molecular weight */ 24 | float heat; /* heat of formation at 298.15 K (J/mol) */ 25 | double dho; /* HO(298.15) - HO(0) */ 26 | float range[4][2]; /* temperature range */ 27 | int ncoef[4]; /* number of coefficient for Cp0/R */ 28 | int ex[4][8]; /* exponent in empirical equation */ 29 | 30 | double param[4][9]; 31 | 32 | /* for species with data at only one temperature */ 33 | /* especially condensed */ 34 | float temp; 35 | float enth; 36 | 37 | } thermo_t; 38 | 39 | /*************************************************************** 40 | TYPE: Structure to hold information of species contain in the 41 | propellant data file 42 | ****************************************************************/ 43 | typedef struct _propellant 44 | { 45 | char name[120]; /* name of the propellant */ 46 | int elem[6]; /* element in the molecule (atomic number) max 6 */ 47 | int coef[6]; /* stochiometric coefficient of this element 48 | (0 for none) */ 49 | float heat; /* heat of formation in Joule/gram */ 50 | float density; /* density in g/cubic cm */ 51 | 52 | } propellant_t; 53 | 54 | 55 | extern propellant_t *propellant_list; 56 | extern thermo_t *thermo_list; 57 | 58 | extern const float molar_mass[]; 59 | extern const char symb[][3]; 60 | 61 | extern unsigned long num_thermo; 62 | extern unsigned long num_propellant; 63 | 64 | /************************************************************* 65 | FUNCTION: Search in the field name of thermo_list and return 66 | the value of the found item. 67 | 68 | PARAMETER: A string corresponding to what we search, 69 | example: "CO2" 70 | 71 | COMMENTS: If nothing is found, it return -1 72 | 73 | AUTHOR: Antoine Lefebvre 74 | modification bye Mark Pinese 75 | **************************************************************/ 76 | int thermo_search(char *str); 77 | 78 | int propellant_search(char *str); 79 | 80 | int atomic_number(char *symbole); 81 | 82 | int propellant_search_by_formula(char *str); 83 | 84 | /************************************************************* 85 | FUNCTION: Return the enthalpy of the molecule in thermo_list[sp] 86 | at the temperature T in K. (Ho/RT) 87 | 88 | PARAMETER: sp is the position in the array of the molecule 89 | T is the temperature in K 90 | 91 | COMMENTS: It use the parametric form explain in the documentation 92 | to compute the value from the data read in the file 93 | thermo.dat 94 | 95 | AUTHOR: Antoine Lefebvre 96 | **************************************************************/ 97 | double enthalpy_0(int sp, float T); 98 | 99 | /************************************************************* 100 | FUNCTION: Return the entropy of the molecule in thermo_list[sp] 101 | at the temperature T in K. (So/RT) 102 | 103 | PARAMETER: sp is the position in the array of the molecule 104 | T is the temperature in K 105 | 106 | COMMENTS: It use the parametric form explain in the documentation 107 | to compute the value from the data read in the file 108 | thermo.dat 109 | 110 | AUTHOR: Antoine Lefebvre 111 | **************************************************************/ 112 | double entropy_0(int sp, float T); 113 | 114 | double entropy(int sp, state_t st, double ln_nj_n, float T, float P); 115 | 116 | /************************************************************* 117 | FUNCTION: Return the specific heat (Cp) of the molecule in 118 | thermo_list[sp] at the temperature T in K. (Cp/RT) 119 | 120 | PARAMETER: sp is the position in the array of the molecule 121 | T is the temperature in K 122 | 123 | COMMENTS: It use the parametric form explain in the documentation 124 | to compute the value from the data read in the file 125 | thermo.dat 126 | 127 | AUTHOR: Antoine Lefebvre 128 | **************************************************************/ 129 | double specific_heat_0(int sp, float T); 130 | 131 | double mixture_specific_heat_0(equilibrium_t *e, double temp); 132 | 133 | /************************************************************* 134 | FUNCTION: Return true if the thermochemical data are define for 135 | this temperature. 136 | 137 | PARAMETER: The same as for entropy 138 | 139 | COMMENTS: It is useful to determine if a specie is present at 140 | a given temperature. 141 | 142 | AUTHOR: Antoine Lefebvre 143 | **************************************************************/ 144 | int temperature_check(int sp, float T); 145 | 146 | double transition_temperature(int sp, float T); 147 | 148 | double propellant_enthalpy(equilibrium_t *e); 149 | double product_enthalpy(equilibrium_t *e); 150 | double product_entropy(equilibrium_t *e); 151 | double propellant_mass(equilibrium_t *e); 152 | 153 | int compute_density(composition_t *c); 154 | 155 | 156 | /************************************************************* 157 | FUNCTION: Return the gibbs free energy of the molecule in 158 | thermo_list[sp] at temperature T. (uo/RT) 159 | 160 | PARAMETER: sp is the position in the array of the molecule 161 | T is the temperature in K 162 | 163 | COMMENTS: g = H - ST where H is the enthalpy, T the temperature 164 | and S the entropy. 165 | **************************************************************/ 166 | double gibbs_0(int sp, float T); 167 | 168 | 169 | /************************************************************* 170 | FUNCTION: Return the gibbs free energy of the molecule in 171 | thermo_list[sp] at temperature T, pressure P. (u/RT) 172 | 173 | PARAMETER: sp is the position in the array of the molecule 174 | T is the temperature in K 175 | 176 | COMMENTS: g = uo + ln(nj/n) + ln(P) for gazes 177 | g = uo for condensed 178 | 179 | AUTHOR: Antoine Lefebvre 180 | **************************************************************/ 181 | //double gibbs(int sp, state_t st, double nj, double n, float T, float P); 182 | double gibbs(int sp, state_t st, double nj_n_n, float T, float P); 183 | 184 | 185 | /*************************************************************** 186 | FUNCTION: Return the heat of formation of a propellant in kJ/mol 187 | ****************************************************************/ 188 | double heat_of_formation(int molecule); 189 | 190 | /************************************************************* 191 | FUNCTION: Return the molar mass of a propellant (g/mol) 192 | 193 | PARAMETER: molecule is the number in propellant_list 194 | **************************************************************/ 195 | double propellant_molar_mass(int molecule); 196 | 197 | 198 | 199 | double propellant_mass(equilibrium_t *e); 200 | 201 | 202 | #endif 203 | -------------------------------------------------------------------------------- /pypropep/cpropep/libthermo/src/Makefile: -------------------------------------------------------------------------------- 1 | 2 | CC = gcc 3 | COPT = -g -Wall -O3 # -pg 4 | 5 | ROOT = ../.. 6 | INCLUDEDIR = -I$(ROOT)/libcpropep/include \ 7 | -I$(ROOT)/libcompat/include \ 8 | -I$(ROOT)/libnum/include \ 9 | -I../include/ 10 | 11 | DEF = -DGCC 12 | 13 | LIBNAME = libthermo.a 14 | 15 | LIBOBJS = load.o thermo.o 16 | 17 | all: $(LIBNAME) 18 | 19 | .c.o: 20 | $(CC) $(DEF) $(INCLUDEDIR) $(COPT) -c $*.c -o $*.o 21 | 22 | $(LIBNAME): $(LIBOBJS) 23 | ar -r $@ $(LIBOBJS) 24 | ranlib $@ 25 | mv $(LIBNAME) ../lib/ 26 | 27 | clean: 28 | rm -f *.o *~ 29 | 30 | deep-clean: clean 31 | rm -f ../lib/$(LIBNAME) 32 | -------------------------------------------------------------------------------- /pypropep/cpropep/libthermo/src/load.c: -------------------------------------------------------------------------------- 1 | #include <stdio.h> 2 | #include <string.h> 3 | #include <stdlib.h> 4 | 5 | #include "equilibrium.h" 6 | #include "load.h" 7 | #include "thermo.h" 8 | 9 | #include "conversion.h" 10 | #include "return.h" 11 | 12 | 13 | /*************************************************************************** 14 | Initial format of thermo.dat: 15 | interval variable type size description 16 | ----------------------------------------------------------------------------- 17 | (0, 18) name string 18 compound name 18 | (18, 73) comments string 55 comment 19 | (73, 75) nint int 2 the number of temperature intervals 20 | (75, 81) id string 6 the material id 21 | 81 state int 1 0 - GAS, else CONDENSED 22 | (82, 95) weight float 13 molecular weight 23 | (95, 108) enth/heat float 13 enthaply if nint == 0 24 | else heat of formation 25 | ... 26 | rest of file 27 | ... 28 | ***************************************************************************/ 29 | 30 | int load_thermo(char *filename) 31 | { 32 | FILE *fd; 33 | 34 | int i = 0; 35 | int j, k, l; 36 | 37 | bool ok; 38 | 39 | char buf[88], *buf_ptr, tmp[32], *tmp_ptr; 40 | buf_ptr = &buf[0]; 41 | tmp_ptr = &tmp[0]; 42 | 43 | /* open the file for reading */ 44 | if ((fd = fopen(filename, "r")) == NULL ) 45 | return ERR_FOPEN; 46 | 47 | if (global_verbose) 48 | { 49 | printf("Scanning thermo data file..."); 50 | fflush(stdout); 51 | } 52 | 53 | num_thermo = 0; 54 | 55 | 56 | /* Scan thermo.dat to find the number of positions in thermo_list 57 | to allocate */ 58 | while (fgets(buf_ptr, 88, fd)) 59 | { 60 | /* 61 | All that is required is to count the number of lines not 62 | starting with ' ', '!' or '-' 63 | */ 64 | if (*buf_ptr != ' ' && *buf_ptr != '!' && *buf_ptr != '-') 65 | num_thermo++; 66 | } 67 | 68 | /* Reset the file pointer */ 69 | fseek(fd, 0, SEEK_SET); 70 | 71 | if (global_verbose) 72 | { 73 | printf("\nScan complete. %ld records found. Allocating memory...", 74 | num_thermo); 75 | } 76 | 77 | if ((thermo_list = (thermo_t *)malloc (sizeof(thermo_t) * num_thermo)) == 78 | NULL) 79 | { 80 | printf("\n\nMemory allocation error with thermo_t thermo_list[%ld], %ld bytes required", num_thermo, sizeof(thermo_t) * num_thermo); 81 | return ERR_MALLOC; 82 | } 83 | 84 | if (global_verbose) 85 | { 86 | printf("\nSuccessful. Loading thermo data file..."); 87 | fflush(stdout); 88 | } 89 | 90 | for (i = 0; i < num_thermo; i++) 91 | { 92 | /* Read in the next line and check for EOF */ 93 | if (!fgets(buf_ptr, 88, fd)) 94 | { 95 | fclose(fd); 96 | free(thermo_list); 97 | return ERR_EOF; 98 | } 99 | 100 | /* Skip commented lines */ 101 | while (*buf_ptr == '!') 102 | { 103 | if (!fgets(buf_ptr, 88, fd)) 104 | { 105 | fclose(fd); 106 | free(thermo_list); 107 | return ERR_EOF; 108 | } 109 | } 110 | 111 | /* Read in the name and the comments */ 112 | strncpy((thermo_list + i)->name, buf_ptr, 18); 113 | trim_spaces((thermo_list + i)->name, 18); 114 | 115 | strncpy((thermo_list + i)->comments, buf_ptr + 18, 55); 116 | trim_spaces((thermo_list + i)->comments, 55); 117 | 118 | // Read in the next line and check for EOF 119 | if (!fgets(buf_ptr, 88, fd)) 120 | { 121 | fclose(fd); 122 | free(thermo_list); 123 | return ERR_EOF; 124 | } 125 | 126 | strncpy(tmp_ptr, buf_ptr, 3); 127 | (thermo_list + i)->nint = atoi(tmp_ptr); 128 | 129 | strncpy((thermo_list + i)->id, buf_ptr + 3, 6); 130 | trim_spaces((thermo_list + i)->id, 6); 131 | 132 | /* get the chemical formula and coefficient */ 133 | /* grep the elements (5 max) */ 134 | for (k = 0; k < 5; k++) 135 | { 136 | tmp[0] = buf[k * 8 + 10]; 137 | tmp[1] = buf[k * 8 + 11]; 138 | tmp[2] = '\0'; 139 | 140 | /* Check for an empty place (no more atoms) */ 141 | if (strcmp(tmp, " ")) 142 | { 143 | /* Atoms still to be processed */ 144 | 145 | /* find the atomic number of the element */ 146 | (thermo_list + i)->elem[k] = atomic_number(tmp); 147 | 148 | /* And the number of atoms */ 149 | strncpy(tmp_ptr, buf_ptr + k * 8 + 13, 6); 150 | tmp[6] = '\0'; 151 | 152 | /* Should this be an int? If so, why is it stored in x.2 format? */ 153 | (thermo_list + i)->coef[k] = (int) atof(tmp_ptr); 154 | } 155 | else 156 | { 157 | /* No atom here */ 158 | (thermo_list + i)->coef[k] = 0; 159 | } 160 | } 161 | 162 | /* grep the state */ 163 | if (buf[51] == '0') 164 | (thermo_list + i)->state = GAS; 165 | else 166 | (thermo_list + i)->state = CONDENSED; 167 | 168 | /* grep the molecular weight */ 169 | strncpy(tmp_ptr, buf_ptr + 52, 13); 170 | tmp[13] = '\0'; 171 | (thermo_list + i)->weight = atof(tmp_ptr); 172 | 173 | /* grep the heat of formation (J/mol) or enthalpy if condensed */ 174 | /* The values are assigned in the if block following */ 175 | strncpy(tmp_ptr, buf_ptr + 65, 15); 176 | tmp[15] = '\0'; 177 | 178 | /* now get the data */ 179 | /* there is '(thermo_list + i)->nint' set of data */ 180 | if ((thermo_list + i)->nint == 0) 181 | { 182 | /* Set the enthalpy */ 183 | (thermo_list + i)->enth = atof(tmp_ptr); 184 | 185 | /* condensed phase, different info */ 186 | /* Read in the next line and check for EOF */ 187 | if (!fgets(buf_ptr, 88, fd)) 188 | { 189 | fclose(fd); 190 | free(thermo_list); 191 | return ERR_EOF; 192 | } 193 | 194 | /* treat the line */ 195 | /* get the temperature of the assigned enthalpy */ 196 | strncpy(tmp_ptr, buf_ptr + 1, 10); 197 | tmp[10] = '\0'; 198 | 199 | (thermo_list + i)->temp = atof(tmp_ptr); 200 | } 201 | else 202 | { 203 | /* Set the heat of formation */ 204 | (thermo_list + i)->heat = atof(tmp_ptr); 205 | 206 | 207 | /* I'm not quite sure this is necessary */ 208 | /* if the value is 0 and this is the same substance as 209 | the previous one but in a different state ... */ 210 | if ((thermo_list + i)->heat == 0 && i != 0) 211 | { 212 | ok = true; 213 | for (j = 0; j < 5; j++) 214 | { 215 | /* set to the same value as the previous one if the same */ 216 | if (!((thermo_list+i)->coef[j] == (thermo_list+i-1)->coef[j] && 217 | (thermo_list+i)->elem[j] == (thermo_list+i-1)->elem[j])) 218 | ok = false; 219 | 220 | } 221 | if (ok) 222 | (thermo_list+i)->heat = (thermo_list+i-1)->heat; 223 | } 224 | 225 | for (j = 0; j < (thermo_list + i)->nint; j++) 226 | { 227 | /* Get the first line of three */ 228 | /* Read in the line and check for EOF */ 229 | if (!fgets(buf_ptr, 88, fd)) 230 | { 231 | fclose(fd); 232 | free(thermo_list); 233 | return ERR_EOF; 234 | } 235 | 236 | /* low */ 237 | strncpy(tmp_ptr, buf_ptr + 1, 10); 238 | tmp[10] = '\0'; 239 | (thermo_list + i)->range[j][0] = atof(tmp_ptr); 240 | 241 | /* high */ 242 | strncpy(tmp_ptr, buf_ptr + 11, 10); 243 | tmp[10] = '\0'; 244 | (thermo_list + i)->range[j][1] = atof(tmp_ptr); 245 | 246 | tmp[0] = buf[22]; 247 | tmp[1] = '\0'; 248 | (thermo_list + i)->ncoef[j] = atoi(tmp_ptr); 249 | 250 | /* grep the exponent */ 251 | for (l = 0; l < 8; l++) 252 | { 253 | strncpy(tmp_ptr, buf_ptr + l * 5 + 23, 5); 254 | tmp[5] = '\0'; 255 | (thermo_list + i)->ex[j][l] = atoi(tmp_ptr); 256 | } 257 | 258 | /* HO(298.15) -HO(0) */ 259 | strncpy(tmp_ptr, buf_ptr + 65, 15); 260 | tmp[15] = '\0'; 261 | (thermo_list + i)->dho = atof(tmp); 262 | 263 | /* Get the second line of three */ 264 | /* Read in the line and check for EOF */ 265 | if (!fgets(buf_ptr, 88, fd)) 266 | { 267 | fclose(fd); 268 | free(thermo_list); 269 | return ERR_EOF; 270 | } 271 | 272 | /* grep the first data line */ 273 | /* there are 5 coefficients */ 274 | for (l = 0; l < 5; l++) 275 | { 276 | strncpy(tmp_ptr, buf_ptr + l * 16, 16); 277 | tmp[16] = '\0'; 278 | 279 | (thermo_list + i)->param[j][l] = atof(tmp_ptr); 280 | //(thermo_list + i)->param[j][l] = strtod(tmp_ptr, NULL); 281 | } 282 | 283 | /* Get the third line of three */ 284 | /* Read in the line and check for EOF */ 285 | if (!fgets(buf_ptr, 88, fd)) 286 | { 287 | fclose(fd); 288 | free(thermo_list); 289 | return ERR_EOF; 290 | } 291 | 292 | /* grep the second data line */ 293 | for (l = 0; l < 2; l++) 294 | { 295 | strncpy(tmp_ptr, buf_ptr + l * 16, 16); 296 | tmp[16] = '\0'; 297 | 298 | (thermo_list + i)->param[j][l + 5] = atof(tmp_ptr); 299 | } 300 | 301 | for (l = 0; l < 2; l++) 302 | { 303 | strncpy(tmp_ptr, buf_ptr + l * 16 + 48, 16); 304 | tmp[16] = '\0'; 305 | 306 | (thermo_list + i)->param[j][l + 7] = atof(tmp_ptr); 307 | } 308 | } 309 | } 310 | } 311 | 312 | fclose(fd); 313 | 314 | if (global_verbose) 315 | printf("%d species loaded.\n", i); 316 | 317 | return i; 318 | } 319 | 320 | 321 | int load_propellant(char *filename) 322 | { 323 | 324 | FILE *fd; 325 | 326 | int i = 0, j, len, name_start, name_end, name_len; 327 | 328 | /* temporary string to store string in order to treat the informations */ 329 | char buf[88], *buf_ptr, tmp[70], *tmp_ptr; 330 | buf_ptr = &buf[0]; 331 | tmp_ptr = &tmp[0]; 332 | 333 | /* open the file for reading */ 334 | if ((fd = fopen(filename, "r")) == NULL ) 335 | return ERR_FOPEN; 336 | 337 | if (global_verbose) 338 | { 339 | printf("Scanning propellant data file..."); 340 | fflush(stdout); 341 | } 342 | 343 | num_propellant = 0; 344 | 345 | /* Scan propellant.dat to find the number of positions in propellant_list 346 | to allocate */ 347 | while (fgets(buf_ptr, 88, fd)) 348 | { 349 | /* All that is required is to count the number of lines not starting 350 | with '*' or '+' */ 351 | if (*buf_ptr != '*' && *buf_ptr != '+') 352 | num_propellant++; 353 | } 354 | 355 | /* Reset the file pointer */ 356 | fseek(fd, 0, SEEK_SET); 357 | 358 | if (global_verbose) 359 | { 360 | printf("\nScan complete. %ld records found. Allocating memory...", 361 | num_propellant); 362 | fflush(stdout); 363 | } 364 | 365 | if ((propellant_list = (propellant_t *) malloc(sizeof(propellant_t) * 366 | num_propellant)) == NULL) 367 | { 368 | printf ("\n\nMemory allocation error with propellant_t propellant_list[%ld], %ld bytes required", num_propellant, sizeof(propellant_t) * num_propellant); 369 | return ERR_MALLOC; 370 | } 371 | 372 | if (global_verbose) 373 | { 374 | printf("\nSuccessful. Loading propellant data file..."); 375 | fflush(stdout); 376 | } 377 | 378 | if (!fgets(buf_ptr, 88, fd)) 379 | { 380 | fclose(fd); 381 | free(propellant_list); 382 | return ERR_EOF; 383 | } 384 | 385 | 386 | for (i = 0; i < num_propellant; i++) 387 | { 388 | /* Skip commented code */ 389 | do 390 | { 391 | if (!fgets(buf_ptr, 88, fd)) 392 | { 393 | fclose(fd); 394 | free(propellant_list); 395 | return ERR_EOF; 396 | } 397 | } 398 | while (*buf_ptr == '*'); 399 | 400 | /* Check for a continued name */ 401 | while (*buf_ptr == '+') 402 | { 403 | /* A continued name found */ 404 | strncpy(tmp_ptr, buf_ptr + 9, 70); 405 | 406 | 407 | /* Find the end of the whitespaces. name_start + 1 is used to leave 408 | one space. */ 409 | for (name_start = 0; name_start < 70; name_start++) 410 | { 411 | if (*(tmp_ptr + name_start + 1) != ' ') 412 | break; 413 | } 414 | 415 | /* Find the end of the name. > 0 is used to be consistent with the 416 | one space left */ 417 | /* when finding name_start */ 418 | for (name_end = 69; name_end > 0; name_end--) 419 | { 420 | if (*(tmp_ptr + name_end) != ' ') 421 | break; 422 | } 423 | 424 | name_len = name_end - name_start + 1; 425 | len = strlen((propellant_list + i - 1)->name); 426 | 427 | /* Check for room in the destination string. Take into account 428 | the possibility of a 429 | multiple line continuation 430 | TODO - > 120 or >= 120 */ 431 | if (len + name_len >= 120) 432 | { 433 | /* Not enough room - copy as much as possible and leave the 434 | name alone */ 435 | strncpy((propellant_list + i - 1)->name + len, 436 | tmp_ptr + name_start, 119 - len); 437 | *((propellant_list + i - 1)->name + 119) = '\x0'; 438 | } 439 | else 440 | { 441 | /* Concatenate the entire string */ 442 | strncpy((propellant_list + i - 1)->name + len, 443 | tmp_ptr + name_start, name_len); 444 | *((propellant_list + i - 1)->name + len + name_len) = '\x0'; 445 | } 446 | 447 | 448 | /* Processing of this line is done, so get the next one */ 449 | if (!fgets(buf_ptr, 88, fd)) 450 | { 451 | fclose(fd); 452 | free(propellant_list); 453 | return ERR_EOF; 454 | } 455 | } 456 | 457 | /* grep the name */ 458 | strncpy((propellant_list + i)->name, buf_ptr + 9, 30); 459 | trim_spaces((propellant_list + i)->name, 30); 460 | 461 | for (j = 0; j < 6; j++) 462 | { 463 | tmp[0] = buf[j * 5 + 39]; 464 | tmp[1] = buf[j * 5 + 40]; 465 | tmp[2] = buf[j * 5 + 41]; 466 | tmp[3] = '\0'; 467 | 468 | (propellant_list + i)->coef[j] = atoi(tmp); 469 | 470 | tmp[0] = buf[j * 5 + 42]; 471 | tmp[1] = buf[j * 5 + 43]; 472 | tmp[2] = '\0'; 473 | 474 | /* find the atomic number of the element */ 475 | /* 476 | for (k = 0; k < N_SYMB; k++) 477 | { 478 | if (!(strcmp(tmp, symb[k]))) 479 | { 480 | (propellant_list + i)->elem[j] = k; 481 | break; 482 | } 483 | } 484 | */ 485 | (propellant_list + i)->elem[j] = atomic_number(tmp); 486 | } 487 | 488 | strncpy(tmp_ptr, buf_ptr + 69, 5); 489 | tmp[5] = '\0'; 490 | propellant_list[i].heat = atof(tmp) * CAL_TO_JOULE; 491 | 492 | strncpy(tmp_ptr, buf_ptr + 75, 5); 493 | tmp[5] = '\0'; 494 | propellant_list[i].density = atof(tmp) * LBS_IN3_TO_G_CM3; 495 | 496 | } 497 | 498 | fclose(fd); 499 | 500 | if (global_verbose) 501 | printf("%d species loaded.\n", i); 502 | 503 | return i; 504 | } 505 | 506 | 507 | void trim_spaces(char *str, unsigned int len) 508 | { 509 | unsigned int i; 510 | 511 | for (i = len - 1; i > 0; i--) 512 | { 513 | if (*(str + i) != ' ' && *(str + i) != '\t') 514 | { 515 | *(str + i + 1) = '\0'; 516 | return; 517 | } 518 | } 519 | *(str + 1) = '\0'; 520 | } 521 | -------------------------------------------------------------------------------- /pypropep/cpropep/libthermo/src/thermo.c: -------------------------------------------------------------------------------- 1 | /* thermo.c - Compute thermodynamic properties of individual 2 | species and composition of species */ 3 | /* $Id: thermo.c,v 1.2 2001/02/22 19:48:44 antoine Exp $ */ 4 | /* Copyright (C) 2000 */ 5 | /* Antoine Lefebvre <antoine.lefebvre@polymtl.ca> */ 6 | /* Mark Pinese <pinese@cyberwizards.com.au> */ 7 | /* */ 8 | /* Licensed under the GPLv2 */ 9 | 10 | 11 | #include <math.h> 12 | #include <string.h> 13 | #include <stdio.h> 14 | #include <ctype.h> 15 | #include <stdlib.h> 16 | 17 | #include "thermo.h" 18 | #include "compat.h" 19 | #include "conversion.h" 20 | 21 | /************************************************************** 22 | These variables hold the number of records for propellant and thermo data 23 | ***************************************************************/ 24 | unsigned long num_propellant, num_thermo; 25 | 26 | /* global variable containing the information about chemical species */ 27 | propellant_t *propellant_list; 28 | thermo_t *thermo_list; 29 | 30 | 31 | /**************************************************************** 32 | VARIABLE: Contain the molar mass of element by atomic number 33 | molar_mass[0] contain hydrogen and so on. 34 | Data come from Sargent-Welch 1996 35 | *****************************************************************/ 36 | const float molar_mass[N_SYMB] = { 37 | 1.00794, 4.002602, 6.941, 9.012182, 10.811, 12.0107, 38 | 14.00674, 15.9994, 18.9984032, 20.11797, 22.989770, 24.305, 39 | 26.981538, 28.0855, 30.973761, 32.066, 35.4527, 39.948, 40 | 39.0983, 40.078, 44.95591, 47.88, 50.9415, 51.996, 41 | 54.938, 55.847, 58.9332, 58.6934, 63.546, 65.39, 42 | 69.723, 72.61, 74.9216, 78.96, 79.904, 83.80, 43 | 85.4678, 87.62, 88.9059, 91.224, 92.9064, 95.94, 44 | 98.0, 101.07, 102.9055, 106.42, 107.868, 112.41, 45 | 114.82, 118.71, 121.757, 127.60, 126.9045, 131.29, 46 | 132.9054, 137.33, 138.9055, 140.12, 140.9077, 144.24, 47 | 145., 150.36, 151.965, 157.25, 158.9253, 162.50, 48 | 164.9303, 167.26, 168.9342, 173.04, 174.967, 178.49, 49 | 180.9479, 183.85, 186.207, 190.2, 192.22, 195.08, 50 | 196.9665, 200.59, 204.383, 207.2, 208.9804, 209., 51 | 210., 222., 223., 226.0254, 227., 232.0381, 52 | 231.0359, 238.029, 237.0482, 244., 12.011, 9.01218, 53 | 10.811, 24.305, 26.98154, 257.0, 0, 2}; 54 | 55 | 56 | /**************************************************************** 57 | VARIABLE: Contain the symbol of the element in the same way as 58 | for the molar mass. 59 | 60 | COMMENTS: It is use in the loading of the data file to recognize 61 | the chemical formula. 62 | *****************************************************************/ 63 | const char symb[N_SYMB][3] = { 64 | "H ","HE","LI","BE","B ","C ","N ","O ", 65 | "F ","NE","NA","MG","AL","SI","P ","S ","CL","AR","K ","CA", 66 | "SC","TI","V ","CR","MN","FE","CO","NI","CU","ZN","GA","GE", 67 | "AS","SE","BR","KR","RB","SR","Y ","ZR","NB","MO","TC","RU", 68 | "RH","PD","AG","CD","IN","SN","SB","TE","I ","XE","CS","BA", 69 | "LA","CE","PR","ND","PM","SM","EU","GD","TB","DY","HO","ER", 70 | "TM","YB","LU","HF","TA","W ","RE","OS","IR","PT","AU","HG","TL", 71 | "PB","BI","PO","AT","RN","FR","RA","AC","TH","PA","U ","NP", 72 | "U6","U5","U1","U2","U3","U4","FM", 73 | "E ", "D " }; /* the E stand for electron and D for deuterium*/ 74 | 75 | 76 | /* Enthalpy in the standard state (Dimensionless) */ 77 | double enthalpy_0(int sp, float T) 78 | { 79 | thermo_t *s = (thermo_list + sp); 80 | 81 | double val; 82 | int pos = 0, i; 83 | 84 | if (T < s->range[0][0]) /* Temperature below the lower range */ 85 | { 86 | pos = 0; 87 | } /*Temperature above the higher range */ 88 | else if (T >= s->range[s->nint-1][1]) 89 | { 90 | pos = s->nint - 1; 91 | } 92 | else 93 | { 94 | for (i = 0; i < s->nint; i++) /* Find the range */ 95 | { 96 | if ((T >= s->range[i][0]) && (T < s->range[i][1])) 97 | pos = i; 98 | } 99 | } 100 | 101 | /* parametric equation for dimentionless enthalpy */ 102 | val = -s->param[pos][0]*pow(T, -2) + s->param[pos][1]*pow(T, -1)*log(T) 103 | + s->param[pos][2] + s->param[pos][3]*T/2 + s->param[pos][4]*pow(T, 2)/3 104 | + s->param[pos][5]*pow(T, 3)/4 + s->param[pos][6]*pow(T, 4)/5 105 | + s->param[pos][7]/T; 106 | 107 | return val; /* dimensionless enthalpy */ 108 | } 109 | 110 | /* Entropy in the standard state (Dimensionless)*/ 111 | double entropy_0(int sp, float T) 112 | { 113 | thermo_t *s = (thermo_list + sp); 114 | double val; 115 | int pos = 0, i; 116 | 117 | if (T < s->range[0][0]) 118 | { 119 | pos = 0; 120 | } 121 | else if (T >= s->range[s->nint-1][1]) 122 | { 123 | pos = s->nint - 1; 124 | } 125 | else 126 | { 127 | for (i = 0; i < s->nint; i++) 128 | { 129 | if ((T >= s->range[i][0]) && (T < s->range[i][1])) 130 | pos = i; 131 | } 132 | } 133 | 134 | /* parametric equation for dimentionless entropy */ 135 | val = -s->param[pos][0]*pow(T, -2)/2 - s->param[pos][1]*pow(T, -1) 136 | + s->param[pos][2]*log(T) + s->param[pos][3]*T 137 | + s->param[pos][4]*pow(T, 2)/2 138 | + s->param[pos][5]*pow(T, 3)/3 + s->param[pos][6]*pow(T, 4)/4 139 | + s->param[pos][8]; 140 | 141 | return val; 142 | } 143 | 144 | /* Specific heat in the standard state (Dimensionless) */ 145 | double specific_heat_0(int sp, float T) 146 | { 147 | thermo_t *s = (thermo_list + sp); 148 | double val; 149 | int pos = 0, i; 150 | 151 | if (T < s->range[0][0]) 152 | { 153 | pos = 0; 154 | } 155 | else if (T >= s->range[s->nint-1][1]) 156 | { 157 | pos = s->nint - 1; 158 | } 159 | else 160 | { 161 | for (i = 0; i < s->nint; i++) 162 | { 163 | if ((T >= s->range[i][0]) && (T < s->range[i][1])) 164 | pos = i; 165 | } 166 | } 167 | 168 | /* parametric equation for dimentionless specific_heat */ 169 | val = s->param[pos][0]*pow(T, -2) + s->param[pos][1]*pow(T, -1) 170 | + s->param[pos][2] + s->param[pos][3]*T + s->param[pos][4]*pow(T, 2) 171 | + s->param[pos][5]*pow(T, 3) + s->param[pos][6]*pow(T, 4); 172 | 173 | return val; 174 | } 175 | 176 | /* Dimensionless Gibbs free energy in the standard state */ 177 | double gibbs_0(int sp, float T) 178 | { 179 | return enthalpy_0(sp, T) - entropy_0(sp, T); /* dimensionless */ 180 | } 181 | 182 | /* Check if the species is in its range of definition 183 | 0 if out of range, 1 if ok */ 184 | int temperature_check(int sp, float T) 185 | { 186 | thermo_t *s = (thermo_list + sp); 187 | 188 | if ((T > s->range[s->nint-1][1]) || (T < s->range[0][0])) 189 | return 0; 190 | 191 | return 1; 192 | } 193 | 194 | /* This function return the transition temperature of the species 195 | considered which is nearest of the temperature T */ 196 | double transition_temperature(int sp, float T) 197 | { 198 | thermo_t *s = (thermo_list + sp); 199 | 200 | /* first assume that the lowest temperature is the good one */ 201 | double transition_T = s->range[0][0]; 202 | 203 | /* verify if we did the good bet */ 204 | if (fabs(transition_T - T) > fabs(s->range[s->nint-1][1] - T)) 205 | { 206 | transition_T = s->range[s->nint-1][1]; 207 | } 208 | 209 | return transition_T; 210 | } 211 | 212 | double entropy(int sp, state_t st, double ln_nj_n, float T, float P) 213 | { 214 | double s; 215 | 216 | switch (st) 217 | { 218 | case GAS: 219 | /* The thermodynamic data are based on a standard state pressure 220 | of 1 bar (10^5 Pa) */ 221 | s = entropy_0(sp, T) - ln_nj_n - log(P * ATM_TO_BAR); 222 | break; 223 | case CONDENSED: 224 | s = entropy_0(sp, T); 225 | break; 226 | default: 227 | s = 0; 228 | } 229 | return s; 230 | } 231 | 232 | 233 | /* J/mol T is in K, P is in atm */ 234 | double gibbs(int sp, state_t st, double ln_nj_n, float T, float P) 235 | { 236 | double g; 237 | 238 | switch (st) 239 | { 240 | case GAS: 241 | g = gibbs_0(sp, T) + ln_nj_n + log(P * ATM_TO_BAR); 242 | break; 243 | case CONDENSED: 244 | g = gibbs_0(sp, T); 245 | break; 246 | default: 247 | g = 0; 248 | } 249 | return g; 250 | } 251 | 252 | double propellant_molar_mass(int molecule) 253 | { 254 | int i = 0, coef; 255 | double ans = 0; 256 | 257 | while ((coef = (propellant_list + molecule)->coef[i])) 258 | { 259 | ans += coef * molar_mass[(propellant_list + molecule)->elem[i]]; 260 | i++; 261 | } 262 | return ans; 263 | } 264 | 265 | /* J/mol */ 266 | double heat_of_formation(int molecule) 267 | { 268 | double hf = (propellant_list + molecule)->heat * 269 | propellant_molar_mass(molecule); 270 | return hf; 271 | } 272 | 273 | 274 | /* should not be in thermo.c */ 275 | double propellant_enthalpy(equilibrium_t *e) 276 | { 277 | int i; 278 | double h = 0.0; 279 | for (i = 0; i < e->propellant.ncomp; i++) 280 | { 281 | h += e->propellant.coef[i] * heat_of_formation (e->propellant.molecule[i]) 282 | / propellant_mass (e); 283 | } 284 | return h; 285 | } 286 | 287 | /* should not be in thermo.c */ 288 | double product_enthalpy(equilibrium_t *e) 289 | { 290 | int i; 291 | double h = 0.0; 292 | 293 | for (i = 0; i < e->product.n[GAS]; i++) 294 | { 295 | h += e->product.coef[GAS][i] * enthalpy_0(e->product.species[GAS][i], e->properties.T); 296 | } 297 | 298 | for (i = 0; i < e->product.n[CONDENSED]; i++) 299 | { 300 | h += e->product.coef[CONDENSED][i] * enthalpy_0(e->product.species[CONDENSED][i], e->properties.T); 301 | } 302 | return h; 303 | } 304 | 305 | /* should not be in thermo.c */ 306 | double product_entropy(equilibrium_t *e) 307 | { 308 | int i; 309 | double ent = 0.0; 310 | for (i = 0; i < e->product.n[GAS]; i++) 311 | { 312 | ent += e->product.coef[GAS][i]*entropy(e->product.species[GAS][i], GAS, 313 | e->itn.ln_nj[i] - e->itn.ln_n, 314 | e->properties.T, e->properties.P); 315 | } 316 | for (i = 0; i < e->product.n[CONDENSED]; i++) 317 | { 318 | ent += e->product.coef[CONDENSED][i]*entropy(e->product.species[CONDENSED][i], 319 | CONDENSED, 0, e->properties.T, e->properties.P); 320 | } 321 | return ent; 322 | } 323 | 324 | /* should not be in thermo.c */ 325 | /* The specific heat of the mixture for frozen performance */ 326 | double mixture_specific_heat_0(equilibrium_t *e, double temp) 327 | { 328 | int i; 329 | double cp = 0.0; 330 | /* for gases */ 331 | for (i = 0; i < e->product.n[GAS]; i++) 332 | { 333 | cp += e->product.coef[GAS][i]*specific_heat_0(e->product.species[GAS][i], temp); 334 | } 335 | /* for condensed */ 336 | for (i = 0; i < e->product.n[CONDENSED]; i++) 337 | { 338 | cp += e->product.coef[CONDENSED][i]* 339 | specific_heat_0(e->product.species[CONDENSED][i], temp); 340 | } 341 | return cp; 342 | } 343 | 344 | 345 | int thermo_search(char *str) 346 | { 347 | int i; 348 | int last = -1; 349 | 350 | for (i = 0; i < num_thermo; i++) 351 | { 352 | if (!(STRNCASECMP(str, (thermo_list + i)->name, strlen(str)))) 353 | { 354 | last = i; 355 | printf("%-5d %s\n", i, (thermo_list + i)->name); 356 | } 357 | } 358 | return last; 359 | } 360 | 361 | int propellant_search(char *str) 362 | { 363 | int i; 364 | int last = -1; 365 | 366 | for (i = 0; i < num_propellant; i++) 367 | { 368 | if (!(STRNCASECMP(str, (propellant_list + i)->name, strlen(str)))) 369 | { 370 | last = i; 371 | printf("%-5d %s\n", i, (propellant_list + i)->name); 372 | } 373 | } 374 | return last; 375 | } 376 | 377 | 378 | int atomic_number(char *symbole) 379 | { 380 | int i; 381 | int element = -1; 382 | 383 | /* find the atomic number of the element */ 384 | for (i = 0; i < N_SYMB; i++) 385 | { 386 | if (!STRCASECMP(symbole, symb[i])) 387 | { 388 | element = i; 389 | break; 390 | } 391 | } 392 | return element; 393 | } 394 | 395 | int compute_density(composition_t *c) 396 | { 397 | short i; 398 | double mass = 0; 399 | 400 | c->density = 0.0; 401 | 402 | for (i = 0; i < c->ncomp; i++) 403 | { 404 | mass += c->coef[i] * propellant_molar_mass(c->molecule[i]); 405 | } 406 | 407 | for (i = 0; i < c->ncomp; i++) 408 | { 409 | if ((propellant_list + c->molecule[i])->density != 0.0) 410 | { 411 | c->density += c->coef[i] * propellant_molar_mass(c->molecule[i]) 412 | / (mass * (propellant_list + c->molecule[i])->density); 413 | } 414 | } 415 | 416 | if (c->density != 0.0) 417 | { 418 | c->density = 1/c->density; 419 | } 420 | 421 | return 0; 422 | } 423 | 424 | /* This fonction return the offset of the molecule in the propellant_list 425 | the argument is the chemical formula of the molecule */ 426 | int propellant_search_by_formula(char *str) 427 | { 428 | int i = 0, j ; 429 | 430 | char tmp[5]; 431 | char *ptr; 432 | 433 | int elem[6] = {0, 0, 0, 0, 0, 1}; 434 | int coef[6] = {0, 0, 0, 0, 0, 0}; 435 | 436 | int molecule = -1; 437 | 438 | ptr = str; /* beginning of the string */ 439 | 440 | while ( (i < 6) && ((ptr - str) < strlen(str)) ) 441 | { 442 | if (isupper(*ptr) && islower(*(ptr+1)) && (isupper(*(ptr+2)) || 443 | iscntrl(*(ptr+2))) ) 444 | { 445 | tmp[0] = *ptr; 446 | tmp[1] = toupper(*(ptr+1)); 447 | tmp[2] = '\0'; 448 | /* find the atomic number of the element */ 449 | elem[i] = atomic_number(tmp); 450 | coef[i] = 1; 451 | i++; 452 | ptr += 2; 453 | } 454 | else if (isupper(*ptr) && (isupper(*(ptr+1)) || 455 | iscntrl(*(ptr+1))) ) 456 | { 457 | tmp[0] = *ptr; 458 | tmp[1] = ' '; 459 | tmp[2] = '\0'; 460 | elem[i] = atomic_number(tmp); 461 | coef[i] = 1; 462 | i++; 463 | ptr++; 464 | } 465 | else if (isupper(*ptr) && isdigit(*(ptr+1))) 466 | { 467 | tmp[0] = *ptr; 468 | tmp[1] = ' '; 469 | tmp[2] = '\0'; 470 | elem[i] = atomic_number(tmp); 471 | 472 | j = 0; 473 | do 474 | { 475 | tmp[j] = *(ptr + 1 + j); 476 | j++; 477 | } while (isdigit(*(ptr + 1 + j))); 478 | 479 | tmp[j] = '\0'; 480 | 481 | coef[i] = atoi(tmp); 482 | i++; 483 | 484 | ptr = ptr + j + 1; 485 | } 486 | else if (isupper(*ptr) && islower(*(ptr+1)) && isdigit(*(ptr+2))) 487 | { 488 | tmp[0] = *ptr; 489 | tmp[1] = toupper(*(ptr+1)); 490 | tmp[2] = '\0'; 491 | elem[i] = atomic_number(tmp); 492 | 493 | j = 0; 494 | while (isdigit(*(ptr + 2 + j))) 495 | { 496 | tmp[j] = *(ptr + 1 + j); 497 | j++; 498 | } 499 | tmp[j] = '\0'; 500 | 501 | coef[i] = atoi(tmp); 502 | i++; 503 | 504 | ptr = ptr + j + 2; 505 | } 506 | } 507 | 508 | /* 509 | for (i = 0; i < 6; i++) 510 | { 511 | if (elem[i] != -1) 512 | printf("%s %d\n", symb[elem[i]], coef[i]); 513 | } 514 | */ 515 | 516 | for (i = 0; i < num_propellant; i++) 517 | { 518 | for (j = 0; j < 6; j++) 519 | { 520 | /* set to the same value as the previous one if the same */ 521 | if (!( ((propellant_list+i)->coef[j] == coef[j]) && 522 | ((propellant_list+i)->elem[j] == elem[j]) )) 523 | break; 524 | } 525 | 526 | 527 | /* Now search in propellant list for this molecule */ 528 | /* 529 | for (j = 0; j < num_propellant; j++) 530 | { 531 | for (i = 0; i < 6; i++) 532 | { 533 | if ( (coef[i] != propellant_element_coef(elem[i], j)) && 534 | (propellant_list + i) 535 | break; 536 | } 537 | */ 538 | 539 | if (j == 5) /* we found the molecule ! */ 540 | { 541 | 542 | /* check if the inverse is true */ 543 | molecule = i; 544 | break; 545 | } 546 | } 547 | 548 | return molecule; 549 | } 550 | 551 | 552 | /* Mass of propellant in gram */ 553 | double propellant_mass(equilibrium_t *e) 554 | { 555 | int i; 556 | double mass = 0.0; 557 | for (i = 0; i < e->propellant.ncomp; i++) 558 | { 559 | mass += e->propellant.coef[i] * 560 | propellant_molar_mass(e->propellant.molecule[i]); 561 | } 562 | return mass; 563 | } 564 | -------------------------------------------------------------------------------- /pypropep/data/references.txt: -------------------------------------------------------------------------------- 1 | The data contain in thermo.dat come from the NASA Gleen research center 2 | and was provide by Bonnie McBride. 3 | 4 | The file propellant.dat come from the package of propep. 5 | The following entrie have been add to propellant.dat by R.Nakka. 6 | 7 | -------------------------------------------------------- 8 | 9 | PEPCODED.DAF file revision 1.0 by R.Nakka 7 Nov. 1999 10 | 11 | The following entries have been added to the pepcoded.daf file: 12 | 13 | 1093 DEXTROSE (GLUCOSE) 6C 12H 6O 0 0 0 -1689 .0567] 14 | 1094 SORBITOL 6C 14H 6O 0 0 0 -1776 .0531] 15 | 1095 BITUMEN (ASPHALT) 84C 11H 3O 1N 1S 0 -351 .0400] 16 | 1096 CHARCOAL (OAK) 71C 3H 13O 1N 0 0 -396 .0206] 17 | 1097 CHARCOAL (MAPLE) 79C 3H 14O 0 0 0 -222 .0206] 18 | 1098 EICOSANE (PARAFFIN) 20C 42H 0 0 0 0 -469 .0000] 19 | 20 | References: 21 | 1093,1094,1098: NIST WebBook http://webbook.nist.gov/chemistry 22 | 1095-1097: Journal of Pyrotechnics, Issue No.9 (1999) 23 | 24 | 25 | -------------------------------------------------------------------------------- /pypropep/equilibrium.py: -------------------------------------------------------------------------------- 1 | import re 2 | import numpy as np 3 | import operator 4 | from .cpropep._cpropep import ffi, lib 5 | from .error import RET_ERRORS 6 | 7 | __all__ = ['Equilibrium'] 8 | 9 | class Equilibrium(object): 10 | def __init__(self, equilibrium_t_ptr=None): 11 | super(Equilibrium, self).__init__() 12 | if equilibrium_t_ptr is not None: 13 | self._equil = equilibrium_t_ptr 14 | else: 15 | self._equil = ffi.new("equilibrium_t *") 16 | lib.initialize_equilibrium(self._equil) 17 | self.reset() 18 | 19 | def reset(self): 20 | lib.reset_equilibrium(self._equil) 21 | self._composition = dict() 22 | self._composition_condensed = dict() 23 | self.propellants = [] 24 | 25 | def __del__(self): 26 | del self._equil 27 | 28 | def add_propellant(self, propellant, mol): 29 | ''' 30 | Addes propellant to the pre-equilibrium mixture. 31 | propellant must be a Propellant instance and mol is the number of 32 | mols. 33 | ''' 34 | try: 35 | lib.add_in_propellant(self._equil, propellant['id'], mol) 36 | self.propellants.append(propellant) 37 | except: 38 | # TODO: Write tests for this case! 39 | TypeError("Problem adding propellant. \ 40 | Did you pass in a Propellant object?") 41 | 42 | def add_propellants(self, propellant_list): 43 | ''' 44 | Addes a list of propellants to the pre-equilibrium mixture. 45 | propellant_list is a list of (propellant, mol) tuples as are passed 46 | in to add_propellant(). Example: 47 | add_propellants([ 48 | (pypropep.PROPELLANTS['METHANE'], 1.0), 49 | (pypropep.PROPELLANTS['OXYGEN (GAS)'], 1.0)]) 50 | ''' 51 | for p, m in propellant_list: 52 | self.add_propellant(p, m) 53 | 54 | def add_propellants_by_mass(self, propellant_list): 55 | ''' 56 | Addes a list of propellants to the pre-equilibrium mixture by mass. 57 | This is equivalent to add_propellants except that quantity is specified 58 | by mass not mols. 59 | propellant_list is a list of (propellant, mol) tuples as are passed 60 | in to add_propellant(). Example: 61 | add_propellants([ 62 | (pypropep.PROPELLANTS['METHANE'], 1.0), 63 | (pypropep.PROPELLANTS['OXYGEN (GAS)'], 1.0)]) 64 | ''' 65 | propellant_list_by_mol = [] 66 | for p, w in propellant_list: 67 | N = w / p.mw 68 | propellant_list_by_mol.append((p, N)) 69 | self.add_propellants(propellant_list_by_mol) 70 | 71 | @property 72 | def properties_computed(self): 73 | return bool(self._equil.properties_ok) 74 | 75 | @property 76 | def performance_computed(self): 77 | return bool(self._equil.performance_ok) 78 | 79 | @property 80 | def equilibrated(self): 81 | return bool(self._equil.equilibrium_ok) 82 | 83 | @property 84 | def properties(self): 85 | return self._equil.properties 86 | 87 | @property 88 | def composition(self): 89 | if self.equilibrated is False: 90 | return None 91 | return self._composition 92 | 93 | @property 94 | def composition_sorted(self): 95 | if self.equilibrated is False: 96 | return None 97 | return sorted(list(self._composition.items()), key=operator.itemgetter(1), 98 | reverse=True) 99 | 100 | @property 101 | def composition_condensed(self): 102 | if self.equilibrated is False: 103 | return None 104 | return self._composition_condensed 105 | 106 | 107 | def _compute_product_composition(self): 108 | if self.equilibrated is False: 109 | raise RuntimeError("Can't compute product composition until \ 110 | equilibrum is computed") 111 | mol_g = self._equil.itn.n 112 | for i in range(self._equil.product.n[lib.CONDENSED]): 113 | mol_g += self._equil.product.coef[lib.CONDENSED][i] 114 | 115 | for i in range(self._equil.product.n[lib.GAS]): 116 | ind = self._equil.product.species[lib.GAS][i] 117 | name = ffi.string(lib.thermo_list[ind].name).decode('utf-8') 118 | self._composition[name] = \ 119 | self._equil.product.coef[lib.GAS][i] / mol_g 120 | 121 | for i in range(self._equil.product.n[lib.CONDENSED]): 122 | ind = self._equil.product.species[lib.CONDENSED][i] 123 | name = ffi.string(lib.thermo_list[ind].name).decode('utf-8') 124 | self._composition_condensed[name] = \ 125 | self._equil.product.coef[lib.CONDENSED][i] / mol_g 126 | 127 | def set_state(self, P, T=None, type='HP'): 128 | ''' 129 | Set state for equillibrium calculation. Note that type 130 | must be in ('TP', 'SP', 'HP'). If 'TP' temperature must 131 | be specified; otherwise it must not be. 132 | ''' 133 | self._equil.product.n[lib.CONDENSED] = 0 134 | if type == 'TP': 135 | if T is None: 136 | raise ValueError("set_state: Temperature must be specified \ 137 | for mode 'TP'") 138 | self.properties.T = T 139 | self.properties.P = P 140 | eq_type = lib.TP 141 | 142 | elif type == 'HP' or type == 'SP': 143 | if T is not None: 144 | raise ValueError("set_state: Temperature must not be specified \ 145 | for mode 'HP' or 'SP'") 146 | self.properties.P = P 147 | if type == 'HP': 148 | eq_type = lib.HP 149 | else: 150 | eq_type = lib.SP 151 | 152 | else: 153 | raise ValueError("set_state: type must be one of ('TP', 'SP', 'HP')!") 154 | 155 | err = lib.equilibrium(self._equil, eq_type) 156 | if err != 0: 157 | raise RuntimeError("Equilibrium failed with error: '{}'".format( 158 | RET_ERRORS[err])) 159 | 160 | self._compute_product_composition() 161 | 162 | @property 163 | def state_str(self): 164 | s = "Pressure: {:.3f} atm \n".format(self.properties.P) 165 | s += "Temperature: {:.1f} K \n".format(self.properties.T) 166 | s += "Enthalpy: {:.3f} kJ/kg \n".format(self.properties.H) 167 | s += "Int. Energy: {:.3f} kJ/kg \n".format(self.properties.U) 168 | s += "Gibbs Free Energy: {:.3f} kJ/kg \n".format(self.properties.G) 169 | s += "Entropy: {:.3f} kJ/kg-K \n".format(self.properties.S) 170 | s += "Molar Mass: {:.3f} g/mol \n".format(self.properties.M) 171 | s += "dV_P: {:.3f}\n".format(self.properties.dV_P) 172 | s += "dV_T: {:.3f}\n".format(self.properties.dV_T) 173 | s += "Cp: {:.3f} kJ/kg-K\n".format(self.properties.Cp) 174 | s += "Cv: {:.3f} kJ/kg-K\n".format(self.properties.Cv) 175 | s += "gamma: {:.3f}\n".format(self.properties.Isex) 176 | s += "Sound Speed: {:.1f} m/s\n\n".format(self.properties.Vson) 177 | return s 178 | 179 | def __str__(self): 180 | s = "Status:\n" 181 | s += "\tEquillibrium Computed: {}\n".format(str(self.equilibrated)) 182 | s += "\tProperties Computed: {}\n".format(str(self.properties_computed)) 183 | s += "\tPerformance Computed: {}\n".format(str(self.performance_computed)) 184 | s += "Composition:\n" 185 | for i in range(self._equil.propellant.ncomp): 186 | ind = self._equil.propellant.molecule[i] 187 | name = ffi.string(lib.propellant_list[ind].name).decode('utf-8') 188 | s += "\t{} - {:.3f} mol\n".format(name, 189 | self._equil.propellant.coef[i]) 190 | s += "State:\n" 191 | s += "\t" + re.sub(r"(\n)", r"\1\t", self.state_str) 192 | return s 193 | 194 | def __repr__(self): 195 | return self.__str__() 196 | -------------------------------------------------------------------------------- /pypropep/error.py: -------------------------------------------------------------------------------- 1 | RET_ERRORS = { 2 | -1: "Malloc Error", 3 | -2: "FOPEN Error", 4 | -3: "EOF Error", 5 | -4: "Not allocated Error", 6 | -5: "Too much product error", 7 | -6: "Equlibrium error", 8 | -7: "Area ratio error", 9 | -8: "Ratio type error", 10 | -9: "Too many equilibrium iterations" 11 | } 12 | -------------------------------------------------------------------------------- /pypropep/performance.py: -------------------------------------------------------------------------------- 1 | import re 2 | from .cpropep._cpropep import ffi, lib 3 | from pypropep.equilibrium import Equilibrium 4 | from pypropep.error import RET_ERRORS 5 | 6 | __all__ = ['RocketPerformance', 'FrozenPerformance', 'ShiftingPerformance'] 7 | 8 | Ge = 9.80665 9 | 10 | class RocketPerformance(object): 11 | ''' 12 | A generic container class for cpropep case's. 13 | The GenericCase is equivalent to the "TP" option in cpropep where 14 | temperature and pressure are specified for the equillibrium calculation. 15 | ''' 16 | def __init__(self, T=300., P=1.): 17 | super(RocketPerformance, self).__init__() 18 | self._equil_structs = ffi.new("equilibrium_t[3]") 19 | self._equil_objs = list() 20 | for i in range(3): 21 | e = ffi.addressof(self._equil_structs[i]) 22 | self._equil_objs.append(Equilibrium(e)) 23 | 24 | @property 25 | def equilibrated(self): 26 | equilibrated = False 27 | for i in range(3): 28 | equilibrated = equilibrated and self._equil_objs[i].equilibrated 29 | return equilibrated 30 | 31 | @property 32 | def properties_computed(self): 33 | properties_computed = False 34 | for i in range(3): 35 | properties_computed = properties_computed and \ 36 | self._equil_objs[i].properties_computed 37 | return properties_computed 38 | 39 | @property 40 | def performance_computed(self): 41 | computed = False 42 | for i in range(3): 43 | computed = computed and self._equil_objs[i].performance_computed 44 | return computed 45 | 46 | @property 47 | def properties(self): 48 | return [p.properties for p in self._equil_structs] 49 | 50 | @property 51 | def composition(self): 52 | return { 53 | "chamber" : self._equil_objs[0].composition_sorted, 54 | "throat" : self._equil_objs[1].composition_sorted, 55 | "exit" : self._equil_objs[2].composition_sorted 56 | } 57 | 58 | @property 59 | def composition_condensed(self): 60 | return { 61 | "chamber" : self._equil_objs[0].composition_condensed, 62 | "throat" : self._equil_objs[1].composition_condensed, 63 | "exit" : self._equil_objs[2].composition_condensed 64 | } 65 | 66 | @property 67 | def performance(self): 68 | return self._equil_structs[2].performance 69 | 70 | 71 | def add_propellant(self, propellant, mol): 72 | ''' 73 | Addes propellant to the pre-equilibrium mixture. 74 | propellant must be a Propellant instance and mol is the number of 75 | mols. 76 | ''' 77 | self._equil_objs[0].add_propellant(propellant, mol) 78 | 79 | def add_propellants(self, propellant_list): 80 | ''' 81 | Addes a list of propellants to the pre-equilibrium mixture. 82 | propellant_list is a list of (propellant, mol) tuples as are passed 83 | in to add_propellant(). Example: 84 | add_propellants([ 85 | (pypropep.PROPELLANTS['METHANE'], 1.0), 86 | (pypropep.PROPELLANTS['OXYGEN (GAS)'], 1.0)]) 87 | ''' 88 | self._equil_objs[0].add_propellants(propellant_list) 89 | 90 | def add_propellants_by_mass(self, propellant_list): 91 | ''' 92 | Addes a list of propellants to the pre-equilibrium mixture by mass. 93 | This is equivalent to add_propellants except that quantity is specified 94 | by mass not mols. 95 | propellant_list is a list of (propellant, mol) tuples as are passed 96 | in to add_propellant(). Example: 97 | add_propellants([ 98 | (pypropep.PROPELLANTS['METHANE'], 1.0), 99 | (pypropep.PROPELLANTS['OXYGEN (GAS)'], 1.0)]) 100 | ''' 101 | propellant_list_by_mol = [] 102 | for p, w in propellant_list: 103 | N = w / p.mw 104 | propellant_list_by_mol.append((p, N)) 105 | self.add_propellants(propellant_list_by_mol) 106 | 107 | def set_state(self): 108 | for e in self._equil_objs: 109 | e._compute_product_composition() 110 | 111 | def __str__(self): 112 | s = "Status:\n" 113 | s += "\tEquillibrium Computed: {}\n".format(str(self.equilibrated)) 114 | s += "\tProperties Computed: {}\n".format(str(self.properties_computed)) 115 | s += "\tPerformance Computed: {}\n".format(str(self.performance_computed)) 116 | s += "Composition:\n" 117 | for i in range(self._equil_structs[0].propellant.ncomp): 118 | ind = self._equil_structs[0].propellant.molecule[i] 119 | name = ffi.string(lib.propellant_list[ind].name).decode('utf-8') 120 | s += "\t{} - {:.3f} mol\n".format(name, 121 | self._equil_structs[0].propellant.coef[i]) 122 | 123 | for i,c in enumerate([ 124 | '======= Chamber =======', 125 | '======= Throat =======', 126 | '======= Exit =======']): 127 | s += "{0}: \n".format(c) 128 | s += "\t" + re.sub(r"(\n)", r"\1\t", self._equil_objs[i].state_str) 129 | s += "Ae/At: {:.5f}\n".format(self._equil_structs[i].performance.ae_at) 130 | s += "\tA/dotm: {:.5f} m/s/atm\n".format(self._equil_structs[i].performance.a_dotm) 131 | s += "\tC*: {:.5f} m/s\n".format(self._equil_structs[i].performance.cstar) 132 | s += "\tCf: {:.5f}\n".format(self._equil_structs[i].performance.cf) 133 | s += "\tIvac (m/s): {:.5f}\n".format(self._equil_structs[i].performance.Ivac) 134 | s += "\tIsp (m/s): {:.5f}\n".format(self._equil_structs[i].performance.Isp) 135 | s += "\tIsp/g (s): {:.5f}\n\n".format(self._equil_structs[i].performance.Isp/Ge) 136 | 137 | return s 138 | 139 | class FrozenPerformance(RocketPerformance): 140 | def __init__(self, *args): 141 | super(FrozenPerformance, self).__init__(*args) 142 | 143 | def set_state(self, P, Pe=None, Ae_At=None): 144 | if (Pe is not None) and (Ae_At is not None): 145 | raise RuntimeError("Only one of Pe or At_Ae may be set at a time") 146 | 147 | self._equil_structs[0].properties.P = P 148 | 149 | if (Pe is None) and (Ae_At is None): 150 | raise RuntimeError("At least one of Pe or Ae_At must be specified") 151 | elif Pe is not None: 152 | err = lib.frozen_performance(ffi.addressof(self._equil_structs[0]), 153 | lib.PRESSURE, Pe) 154 | elif Ae_At is not None: 155 | err = lib.frozen_performance(ffi.addressof(self._equil_structs[0]), 156 | lib.SUPERSONIC_AREA_RATIO, Ae_At) 157 | else: 158 | raise RuntimeError("Shouldn't have gotten here") 159 | 160 | if err < 0: 161 | raise RuntimeError("Frozen performance failed with {}".format( 162 | RET_ERRORS[err])) 163 | 164 | super(FrozenPerformance, self).set_state() 165 | 166 | 167 | class ShiftingPerformance(RocketPerformance): 168 | def __init__(self, *args): 169 | super(ShiftingPerformance, self).__init__(*args) 170 | 171 | def set_state(self, P, Pe=None, Ae_At=None): 172 | if (Pe is not None) and (Ae_At is not None): 173 | raise RuntimeError("Only one of Pe or At_Ae may be set at a time") 174 | 175 | self._equil_structs[0].properties.P = P 176 | 177 | if (Pe is None) and (Ae_At is None): 178 | raise RuntimeError("At least one of Pe or Ae_At must be specified") 179 | elif Pe is not None: 180 | err = lib.shifting_performance(ffi.addressof(self._equil_structs[0]), 181 | lib.PRESSURE, Pe) 182 | elif Ae_At is not None: 183 | err = lib.shifting_performance(ffi.addressof(self._equil_structs[0]), 184 | lib.SUPERSONIC_AREA_RATIO, Ae_At) 185 | else: 186 | raise RuntimeError("Shouldn't have gotten here") 187 | 188 | if err < 0: 189 | raise RuntimeError("Frozen performance failed with {}".format( 190 | RET_ERRORS[err])) 191 | 192 | super(ShiftingPerformance, self).set_state() 193 | -------------------------------------------------------------------------------- /pypropep/propellant.py: -------------------------------------------------------------------------------- 1 | from .cpropep._cpropep import ffi, lib 2 | 3 | __all__ = ['Propellant'] 4 | 5 | EL_SYMBOLS = [ 6 | "H", "HE", "LI", "BE", "B", "C", "N", "O", 7 | "F", "NE", "NA", "MG", "AL", "SI", "P", "S", "CL", "AR", "K", "CA", 8 | "SC", "TI", "V", "CR", "MN", "FE", "CO", "NI", "CU", "ZN", "GA", "GE", 9 | "AS", "SE", "BR", "KR", "RB", "SR", "Y", "ZR", "NB", "MO", "TC", "RU", 10 | "RH", "PD", "AG", "CD", "IN", "SN", "SB", "TE", "I ", "XE", "CS", "BA", 11 | "LA", "CE", "PR", "ND", "PM", "SM", "EU", "GD", "TB", "DY", "HO", "ER", 12 | "TM", "YB", "LU", "HF", "TA", "W ", "RE", "OS", "IR", "PT", "AU", "HG", "TL", 13 | "PB", "BI", "PO", "AT", "RN", "FR", "RA", "AC", "TH", "PA", "U", "NP", 14 | "U6", "U5", "U1", "U2", "U3", "U4", "FM", 15 | "E", "D" ] 16 | 17 | 18 | EL_MOLAR_MASS = [ 19 | 1.00794, 4.002602, 6.941, 9.012182, 10.811, 12.0107, 20 | 14.00674, 15.9994, 18.9984032, 20.11797, 22.989770, 24.305, 21 | 26.981538, 28.0855, 30.973761, 32.066, 35.4527, 39.948, 22 | 39.0983, 40.078, 44.95591, 47.88, 50.9415, 51.996, 23 | 54.938, 55.847, 58.9332, 58.6934, 63.546, 65.39, 24 | 69.723, 72.61, 74.9216, 78.96, 79.904, 83.80, 25 | 85.4678, 87.62, 88.9059, 91.224, 92.9064, 95.94, 26 | 98.0, 101.07, 102.9055, 106.42, 107.868, 112.41, 27 | 114.82, 118.71, 121.757, 127.60, 126.9045, 131.29, 28 | 132.9054, 137.33, 138.9055, 140.12, 140.9077, 144.24, 29 | 145., 150.36, 151.965, 157.25, 158.9253, 162.50, 30 | 164.9303, 167.26, 168.9342, 173.04, 174.967, 178.49, 31 | 180.9479, 183.85, 186.207, 190.2, 192.22, 195.08, 32 | 196.9665, 200.59, 204.383, 207.2, 208.9804, 209., 33 | 210., 222., 223., 226.0254, 227., 232.0381, 34 | 231.0359, 238.029, 237.0482, 244., 12.011, 9.01218, 35 | 10.811, 24.305, 26.98154, 257.0, 0, 2] 36 | 37 | class Propellant(dict): 38 | ''' 39 | Thin shell around attrdict for propellant type. 40 | Purpose of a new class is so we can do instance check 41 | elsewhere. 42 | ''' 43 | def __init__(self, *args, **kwargs): 44 | super(Propellant, self).__init__(*args, **kwargs) 45 | 46 | def formula(self, tex=False): 47 | elem = self['elem'] 48 | coef = self['coef'] 49 | code = "" 50 | for e,c in zip(elem, coef): 51 | if c > 0: 52 | if tex is True: 53 | code += "{0}_{{{1}}}".format(EL_SYMBOLS[e], c) 54 | else: 55 | code += "{0}{1}".format(EL_SYMBOLS[e], c) 56 | return code 57 | 58 | 59 | def atoms_of(self, element): 60 | ''' 61 | Looks up and returns the number of atoms of 62 | element in the propellant 63 | ''' 64 | elem = self['elem'] 65 | coef = self['coef'] 66 | if element in EL_SYMBOLS: 67 | el_ind = EL_SYMBOLS.index(element) 68 | else: 69 | print('blah') 70 | RuntimeWarning("Element {0} does not exist".format(element)) 71 | return 0 72 | 73 | if el_ind in elem: 74 | el_ind = elem.index(el_ind) 75 | return coef[el_ind] 76 | 77 | return 0 78 | 79 | @property 80 | def mw(self): 81 | mw = 0. 82 | elem = self['elem'] 83 | coef = self['coef'] 84 | for e,c in zip(elem, coef): 85 | if c > 0: 86 | mw += c * EL_MOLAR_MASS[e] 87 | return mw 88 | 89 | def __str__(self): 90 | return "Propellant: {} - {} [{}]".format(self.formula(), 91 | self['name'], 92 | self['id']) 93 | 94 | def __repr__(self): 95 | return self.__str__() 96 | -------------------------------------------------------------------------------- /reference/RP-1311.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonnydyer/pypropep/ed94c4518689031ca035212c13d7a88c008b5860/reference/RP-1311.pdf -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Python interface to cpropep 3 | ''' 4 | from setuptools import setup 5 | 6 | setup( 7 | name="pypropep", 8 | version='0.1.4', 9 | description='Python wrapper for cpropep rocket performance tool', 10 | license='GPLv3', 11 | author='Jonny Dyer', 12 | author_email='jonny.dyer@gmail.com', 13 | url='https://github.com/jonnydyer/pypropep', 14 | packages=['pypropep', 'pypropep.cpropep'], 15 | package_data={"pypropep" : ["data/*.dat"]}, 16 | setup_requires=["cffi>=1.0.0"], 17 | cffi_modules=["cpropep_build.py:ffibuilder"], 18 | install_requires=["cffi>=1.0.0", "attrdict"], 19 | py_modules=['cpropep_build'], 20 | ) 21 | 22 | # from distutils.core import setup 23 | # import cpropep_build 24 | # 25 | # setup(name='pypropep', 26 | # version='0.1', 27 | # description='Python wrapper for cpropep rocket performance tool', 28 | # license='GPLv3', 29 | # author='Jonny Dyer', 30 | # author_email='jonny.dyer@gmail.com', 31 | # packages=['pypropep', 'pypropep.cpropep'], 32 | # package_data={"pypropep" : ["data/*.dat"]}, 33 | # py_modules=['cpropep_build'], 34 | # ext_modules=[cpropep_build.ffibuilder.distutils_extension()] 35 | # ) 36 | 37 | -------------------------------------------------------------------------------- /tests/test_equilibrium.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.fixture 5 | def pypropep(): 6 | import pypropep 7 | pypropep.init() 8 | return pypropep 9 | 10 | 11 | def test_simp_equilibrium(pypropep): 12 | e = pypropep.Equilibrium() 13 | return e.__str__() 14 | 15 | 16 | def test_add_propellant(pypropep): 17 | e = pypropep.Equilibrium() 18 | o2 = pypropep.PROPELLANTS['OXYGEN (GAS)'] 19 | ch4 = pypropep.PROPELLANTS['METHANE'] 20 | e.add_propellants([(o2, 1.), (ch4, 1.)]) 21 | print(e) 22 | 23 | 24 | def test_add_propellants_by_mass(pypropep): 25 | e = pypropep.Equilibrium() 26 | o2 = pypropep.PROPELLANTS['OXYGEN (GAS)'] 27 | ch4 = pypropep.PROPELLANTS['METHANE'] 28 | e.add_propellants_by_mass([(o2, 1.), (ch4, 1.)]) 29 | print(e) 30 | 31 | def test_equil_modes(pypropep): 32 | e = pypropep.Equilibrium() 33 | with pytest.raises(ValueError): 34 | e.set_state(P=1., type='TP') 35 | 36 | with pytest.raises(ValueError): 37 | e.set_state(P=1., T=300., type='HP') 38 | 39 | with pytest.raises(ValueError): 40 | e.set_state(P=1., T=300., type='SP') 41 | 42 | def test_simp_equil(pypropep): 43 | e = pypropep.Equilibrium() 44 | o2 = pypropep.PROPELLANTS['OXYGEN (GAS)'] 45 | ch4 = pypropep.PROPELLANTS['METHANE'] 46 | e.add_propellants([(o2, 1.), (ch4, 1.)]) 47 | e.set_state(P=1.0, T=3000., type='TP') 48 | assert e.equilibrated is True 49 | assert e.properties_computed is True 50 | 51 | def test_TP_composition(pypropep): 52 | e = pypropep.Equilibrium() 53 | n2 = pypropep.PROPELLANTS['NITROGEN (GASEOUS)'] 54 | e.add_propellant(n2, 1.0) 55 | with pytest.raises(RuntimeError): 56 | print(e._compute_product_composition()) 57 | 58 | # Compostion should be None prior to equilibration 59 | assert e.composition is None 60 | assert e.composition_sorted is None 61 | assert e.composition_condensed is None 62 | 63 | # equilibrate 64 | e.set_state(P=1.0, T=273., type='TP') 65 | assert e.equilibrated is True 66 | assert e.properties_computed is True 67 | assert 'N2' in e.composition 68 | for k,v in list(e.composition.items()): 69 | if k == 'N2': 70 | assert v == pytest.approx(1.0, 1e-6) 71 | else: 72 | assert v == pytest.approx(0.0, 1e-6) 73 | 74 | def test_HP_equil(pypropep): 75 | e = pypropep.Equilibrium() 76 | o2 = pypropep.PROPELLANTS['OXYGEN (GAS)'] 77 | ch4 = pypropep.PROPELLANTS['METHANE'] 78 | e.add_propellants([(o2, 1.), (ch4, 1.)]) 79 | e.set_state(P=1.0, type='HP') 80 | assert e.equilibrated is True 81 | assert e.properties_computed is True 82 | assert e.properties.T > 300. 83 | 84 | def test_condensing_equil(pypropep): 85 | kno3 = pypropep.PROPELLANTS['POTASSIUM NITRATE'] 86 | sugar = pypropep.PROPELLANTS['SUCROSE (TABLE SUGAR)'] 87 | p = pypropep.Equilibrium() 88 | p.add_propellants([(kno3, 0.65/kno3.mw), (sugar, 0.35/sugar.mw)]) 89 | p.set_state(P=30) 90 | assert len(p.composition_condensed) > 0 91 | # def test_SP_equil(pypropep): 92 | # e = pypropep.Equilibrium() 93 | # o2 = pypropep.PROPELLANTS['OXYGEN (GAS)'] 94 | # ch4 = pypropep.PROPELLANTS['METHANE'] 95 | # e.add_propellants([(o2, 1.), (ch4, 1.)]) 96 | # e.set_state(P=1.0, type='HP') 97 | # T = e.properties.T 98 | # e.set_state(P=1, type='SP') 99 | # assert e.equilibrated is True 100 | # assert e.properties_computed is True 101 | # assert e.properties.T < 0.5*T 102 | -------------------------------------------------------------------------------- /tests/test_performance.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.fixture 5 | def pypropep(): 6 | import pypropep 7 | pypropep.init() 8 | return pypropep 9 | 10 | 11 | def test_simp_performance(pypropep): 12 | p = pypropep.RocketPerformance() 13 | return p.__str__() 14 | 15 | def test_shifting_performance(pypropep): 16 | p = pypropep.ShiftingPerformance() 17 | lh2 = pypropep.PROPELLANTS['HYDROGEN (CRYOGENIC)'] 18 | lox = pypropep.PROPELLANTS['OXYGEN (LIQUID)'] 19 | # The below is from Sutton pg. 181 20 | OF = 5.551 21 | m_lh2 = 1.0 22 | m_lox = OF 23 | p.add_propellants_by_mass([(lh2, m_lh2), (lox, m_lox)]) 24 | p.set_state(P=53.317*0.986923, Ae_At=25.) 25 | assert p.performance.Isp == pytest.approx(4124, 1e-2) 26 | assert p.performance.Ivac == pytest.approx(4348, 1e-2) 27 | assert p.performance.cstar == pytest.approx(2332.1, 1e-3) 28 | assert p.properties[0].T == pytest.approx(3389, 1e-2) 29 | assert p.properties[2].T == pytest.approx(1468, 1e-2) 30 | assert p.composition['exit'][0][1] > 0.1 31 | assert len(p.composition_condensed['exit']) == 0 32 | 33 | 34 | def test_frozen_performance(pypropep): 35 | p = pypropep.FrozenPerformance() 36 | lh2 = pypropep.PROPELLANTS['HYDROGEN (CRYOGENIC)'] 37 | lox = pypropep.PROPELLANTS['OXYGEN (LIQUID)'] 38 | # The below is from Sutton pg. 181 39 | OF = 5.551 40 | N_lh2 = 1.0 / lh2.mw 41 | N_lox = OF / lox.mw 42 | p.add_propellants([(lh2, N_lh2), (lox, N_lox)]) 43 | p.set_state(P=53.317*0.986923, Ae_At=25.) 44 | #assert p.performance.cstar == pytest.approx(2332.1, 1e-2) 45 | assert p.properties[0].T == pytest.approx(3389, 1e-2) 46 | assert p.composition['exit'][0][1] > 0.1 47 | assert len(p.composition_condensed['exit']) == 0 48 | print(p) 49 | 50 | def test_properties(pypropep): 51 | p = pypropep.FrozenPerformance() 52 | ps = pypropep.ShiftingPerformance() 53 | e = pypropep.Equilibrium() 54 | lh2 = pypropep.PROPELLANTS['RP-1 (RPL)'] 55 | lox = pypropep.PROPELLANTS['OXYGEN (LIQUID)'] 56 | OF = 0.13 57 | p.add_propellants_by_mass([(lh2, 1.0), (lox, OF)]) 58 | p.set_state(P=30, Ae_At=25.) 59 | ps.add_propellants_by_mass([(lh2, 1.0), (lox, OF)]) 60 | ps.set_state(P=30, Ae_At=25.) 61 | e.add_propellants_by_mass([(lh2, 1.0), (lox, OF)]) 62 | e.set_state(P=30, type='HP') 63 | assert p.properties[0].T == pytest.approx(ps.properties[0].T, 1e-2) 64 | assert p.properties[0].Cp == pytest.approx(ps.properties[0].Cp, 1e-2) 65 | assert p.properties[0].Isex == pytest.approx(ps.properties[0].Isex, 1e-2) 66 | assert p.properties[0].Cv == pytest.approx(ps.properties[0].Cv, 1e-2) 67 | 68 | assert p.properties[0].T == pytest.approx(e.properties.T, 1e-2) 69 | assert p.properties[0].Cp == pytest.approx(e.properties.Cp, 1e-2) 70 | assert p.properties[0].Isex == pytest.approx(e.properties.Isex, 1e-2) 71 | assert p.properties[0].Cv == pytest.approx(e.properties.Cv, 1e-2) 72 | -------------------------------------------------------------------------------- /tests/test_propellant.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import os 3 | 4 | @pytest.fixture 5 | def pypropep(): 6 | import pypropep 7 | pypropep.init() 8 | return pypropep 9 | 10 | def test_propellant_str(pypropep): 11 | assert 'Propellant' in pypropep.PROPELLANTS[list(pypropep.PROPELLANTS.keys())[0]].__str__() 12 | 13 | def test_atoms_of(pypropep): 14 | p = pypropep.PROPELLANTS['METHANE'] 15 | assert p.atoms_of('C') == 1 16 | assert p.atoms_of('H') == 4 17 | assert p.atoms_of('N') == 0 18 | assert p.atoms_of('X') == 0 19 | 20 | -------------------------------------------------------------------------------- /tests/test_pypropep.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import os 3 | 4 | @pytest.fixture 5 | def pypropep(): 6 | import pypropep 7 | pypropep.init() 8 | return pypropep 9 | 10 | 11 | def test_propellants_dict(pypropep): 12 | assert len(pypropep.PROPELLANTS) == 1031 13 | 14 | 15 | def test_thermo_dict(pypropep): 16 | assert len(pypropep.SPECIES) == 1921 17 | 18 | 19 | def test_prop_file_override(pypropep): 20 | prop_file = os.path.dirname(pypropep.__file__) + '/data/propellant.dat' 21 | pypropep.init(propellant_file=prop_file) 22 | 23 | 24 | def test_thermo_file_override(pypropep): 25 | therm_file = os.path.dirname(pypropep.__file__) + '/data/thermo.dat' 26 | pypropep.init(thermo_file=therm_file) 27 | 28 | 29 | def test_find_propellant(pypropep): 30 | assert len(pypropep.find_propellant('oxygen')) > 1 31 | assert len(pypropep.find_propellant('OXYGEN')) > 1 32 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py27, py35 3 | 4 | [testenv] 5 | usedevelop = true 6 | passenv = 7 | TRAVIS 8 | TRAVIS_BRANCH 9 | TRAVIS_JOB_ID 10 | deps = pytest 11 | cffi 12 | numpy 13 | coveralls 14 | pytest-cov 15 | commands = 16 | py.test --cov=pypropep --cov-report term-missing -v 17 | coveralls 18 | #[testenv:flake8] 19 | #deps = flake8 20 | #commands = flake8 pypropep tests 21 | 22 | #[flake8] 23 | #exclude = tests/test_depricated.py 24 | --------------------------------------------------------------------------------