From 504699f3757668438a080d90ad14ff55817d6107 Mon Sep 17 00:00:00 2001 From: Weiye Wang Date: Thu, 8 Dec 2022 17:26:08 -0800 Subject: [PATCH] Add all these files to git repository --- Makefile | 98 +++ UserData.cpp | 442 +++++++++++++ UserData.h | 192 ++++++ grid2/Makefile | 47 ++ grid2/gridRoutines.cpp | 244 +++++++ grid2/gridRoutines.d | 1 + grid2/gridRoutines.h | 82 +++ grid2/gridRoutines.o | Bin 0 -> 13576 bytes grid2/gridTest | Bin 0 -> 22600 bytes grid2/main.cpp | 52 ++ grid2/main.d | 1 + grid2/main.o | Bin 0 -> 3424 bytes grid2/parse.cpp | 20 + grid2/parse.d | 1 + grid2/parse.h | 31 + grid2/parse.hpp | 76 +++ grid2/parse.o | Bin 0 -> 2656 bytes gridRoutines.cpp | 244 +++++++ gridRoutines.h | 82 +++ lagrangianCombustion2 | Bin 0 -> 264040 bytes macros.h | 69 ++ main.cpp | 383 +++++++++++ parse.cpp | 20 + parse.h | 31 + parse.hpp | 76 +++ residue.cpp | 1422 ++++++++++++++++++++++++++++++++++++++++ residue.h | 90 +++ restart/Makefile | 6 + restart/read.c | 18 + restart/write.c | 20 + solution.cpp | 90 +++ solution.h | 15 + timing.h | 2 + timing.hpp | 45 ++ 34 files changed, 3900 insertions(+) create mode 100644 Makefile create mode 100644 UserData.cpp create mode 100644 UserData.h create mode 100644 grid2/Makefile create mode 100644 grid2/gridRoutines.cpp create mode 100644 grid2/gridRoutines.d create mode 100644 grid2/gridRoutines.h create mode 100644 grid2/gridRoutines.o create mode 100644 grid2/gridTest create mode 100644 grid2/main.cpp create mode 100644 grid2/main.d create mode 100644 grid2/main.o create mode 100644 grid2/parse.cpp create mode 100644 grid2/parse.d create mode 100644 grid2/parse.h create mode 100644 grid2/parse.hpp create mode 100644 grid2/parse.o create mode 100644 gridRoutines.cpp create mode 100644 gridRoutines.h create mode 100755 lagrangianCombustion2 create mode 100644 macros.h create mode 100644 main.cpp create mode 100644 parse.cpp create mode 100644 parse.h create mode 100644 parse.hpp create mode 100644 residue.cpp create mode 100644 residue.h create mode 100644 restart/Makefile create mode 100644 restart/read.c create mode 100644 restart/write.c create mode 100644 solution.cpp create mode 100644 solution.h create mode 100644 timing.h create mode 100644 timing.hpp diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5dd05d5 --- /dev/null +++ b/Makefile @@ -0,0 +1,98 @@ +#compiler =icpc +#CANTERA_DIR =/opt/scientific/cantera-2.3_intel_mkl +#IDA_DIR =/opt/scientific/sundials-3.1.1_intel_mkl +#EXE =lagrangianCombustion2 +#DESTDIR =~/bin + +compiler =g++ +CANTERA_DIR =/opt/scientific/cantera-2.4_gnu_blas +IDA_DIR =/opt/scientific/sundials-3.1.1_intel_mkl +EXE =DropletCombustion2 +#DESTDIR =~/bin +DESTDIR = ../example + +CANTERA_INCLUDES=-I$(CANTERA_DIR)/include +GSL_INCLUDES =-I/usr/include/gsl +GSL_LIBS =-L/usr/lib -lgsl -lgslcblas +IDA_INCLUDES =-I$(IDA_DIR)/include +SUN_INCLUDES =-I$(IDA_DIR)/include +IDA_LIBS =-L$(IDA_DIR)/lib -lsundials_nvecopenmp \ + -lsundials_ida -lsundials_sunlinsollapackband +CANTERA_LIBS =-L$(CANTERA_DIR)/lib -lcantera_shared +RPATH =-Wl,-rpath=$(IDA_DIR)/lib,-rpath=$(CANTERA_DIR)/lib +RM=rm -f + +compiler?=g++ + +ifeq ($(compiler),g++) +ifeq ($(MAKECMDGOALS),debug) +CPPFLAGS= -Wall -g3 -ggdb +else +CPPFLAGS= -Wall -O3 +endif +CPP=g++ +endif + +ifeq ($(compiler),icpc) + export GXX_INCLUDE=/usr/lib/gcc/x86_64-pc-linux-gnu/7.4.1/include/c++ + CPPFLAGS= -Wall -O3 -gxx-name=/usr/bin/g++-7 -std=c++11 + CPP=icpc +endif + +OBJS = parse.o UserData.o gridRoutines.o residue.o solution.o main.o + +all: $(EXE) + +# pull in dependency info for *existing* .o files +-include $(OBJS:.o=.d) + +parse.o: parse.cpp parse.h parse.hpp + $(CPP) $(CPPFLAGS) -c parse.cpp -o parse.o + $(CPP) -MM parse.cpp > parse.d + +solution.o: solution.cpp solution.h + $(CPP) $(CPPFLAGS) $(CANTERA_INCLUDES) $(IDA_INCLUDES) \ + -c solution.cpp -o solution.o + $(CPP) -MM $(CANTERA_INCLUDES) $(IDA_INCLUDES) \ + solution.cpp > solution.d + +residue.o: residue.cpp residue.h UserData.h gridRoutines.h + $(CPP) $(CPPFLAGS) $(CANTERA_INCLUDES) \ + $(IDA_INCLUDES) $(GSL_INCLUDES) -c residue.cpp \ + -o residue.o -fopenmp + $(CPP) -MM $(CANTERA_INCLUDES) \ + $(IDA_INCLUDES) $(GSL_INCLUDES) \ + residue.cpp > residue.d + +gridRoutines.o: gridRoutines.cpp gridRoutines.h parse.h + $(CPP) $(CPPFLAGS) -c gridRoutines.cpp -o gridRoutines.o + $(CPP) -MM gridRoutines.cpp > gridRoutines.d + +UserData.o: UserData.cpp UserData.h gridRoutines.h parse.h + $(CPP) $(CPPFLAGS) $(CANTERA_INCLUDES) \ + -c UserData.cpp -o UserData.o + $(CPP) -MM $(CANTERA_INCLUDES) \ + UserData.cpp > UserData.d + +main.o: main.cpp UserData.h solution.h residue.h macros.h + $(CPP) $(CPPFLAGS) $(CANTERA_INCLUDES) $(IDA_INCLUDES) \ + -c main.cpp -o main.o + $(CPP) -MM $(CANTERA_INCLUDES) $(IDA_INCLUDES) \ + main.cpp > main.d + +$(EXE): $(OBJS) + $(CPP) $(CPPFLAGS) $(IDA_INCLUDES) \ + $(CANTERA_INCLUDES) $(GSL_INCLUDES) $(RPATH) \ + $(OBJS) -o $(EXE) -fopenmp $(IDA_LIBS) \ + $(CANTERA_LIBS) $(GSL_LIBS) + +debug: $(EXE) + +.PHONY: install +install: + cp $(EXE) $(DESTDIR)/$(EXE) + +.PHONY: clean +clean: + rm -f $(EXE) $(OBJS) *.d + diff --git a/UserData.cpp b/UserData.cpp new file mode 100644 index 0000000..90e5104 --- /dev/null +++ b/UserData.cpp @@ -0,0 +1,442 @@ +#include "UserData.h" +#include "parse.h" + +void freeUserData(UserData data){ + if(data!=NULL){ + if(data->trmix!=NULL){ + delete data->trmix; + printf("Transport Deleted!\n"); + + } + if(data->gas!=NULL){ + delete data->gas; + printf("Gas Deleted!\n"); + + } + if(data->adaptiveGrid){ + if(data->grid->xOld!=NULL){ + delete[] data->grid->xOld; + printf("old grid array Deleted!\n"); + } + if(data->grid->x!=NULL){ + delete[] data->grid->x; + printf("current grid array Deleted!\n"); + } + if(data->grid!=NULL){ + free(data->grid); + printf("grid object Freed!\n"); + } + } + else{ + if(data->uniformGrid!=NULL){ + delete[] data->uniformGrid; + printf("uniformGrid deleted!\n"); + } + } + if(data->innerMassFractions!=NULL){ + delete[] data->innerMassFractions; + printf("innerMassFractions array Deleted!\n"); + } + if(data->output!=NULL){ + fclose(data->output); + printf("Output File Cleared from Memory!\n"); + } + if(data->gridOutput!=NULL){ + fclose(data->gridOutput); + printf("Grid Output File Cleared from Memory!\n"); + } + //if(data->ratesOutput!=NULL){ + // fclose(data->ratesOutput); + // printf("Rates Output File Cleared from Memory!\n"); + //} + if(data->globalOutput!=NULL){ + fclose(data->globalOutput); + printf("Global Output File Cleared from Memory!\n"); + } + + } + free(data); /* Free the user data */ + printf("\n\n"); +} + +UserData allocateUserData(FILE *input){ + + UserData data; + data = (UserData) malloc(sizeof *data); + + if(!data){ + printf("Allocation Failed!\n"); + return(NULL); + } + setSaneDefaults(data); + + int ier; + + ier=parseNumber(input, "basePts" , MAXBUFLEN, &data->npts); + if(ier==-1 || data->npts<=0){ + printf("Enter non-zero basePts!\n"); + return(NULL); + } + + ier=parseNumber(input, "domainLength", MAXBUFLEN, &data->domainLength); + if(ier==-1 || data->domainLength<=0.0e0){ + printf("domainLength error!\n"); + return(NULL); + } + + ier=parseNumber(input, "Rd", MAXBUFLEN, &data->Rd); + if(ier==-1 || data->Rd<=0.0e0){ + printf("Rd error!\n"); + return(NULL); + } + + ier=parseNumber(input, "constantPressure" , MAXBUFLEN, &data->constantPressure); + if(ier==-1 || (data->constantPressure!=0 && data->constantPressure!=1)){ + printf("constantPressure error!\n"); + return(NULL); + } + + ier=parseNumber(input, "problemType" , MAXBUFLEN, &data->problemType); + if(ier==-1 || (data->problemType!=0 + && data->problemType!=1 + && data->problemType!=2 + && data->problemType!=3)){ + printf("include valid problemType!\n"); + printf("0: premixed combustion with NO equilibrated ignition kernel\n"); + printf("1: premixed combustion WITH equilibrated ignition kernel\n"); + printf("2: arbitrary initial conditions\n"); + printf("3: Restart\n"); + return(NULL); + } + + ier=parseNumber(input, "quasiSteady" , MAXBUFLEN, &data->quasiSteady); + if(ier==-1 || (data->quasiSteady!=0 + && data->quasiSteady!=1)){ + printf("include valid quasiSteady!\n"); + printf("0: The droplet surface recedes and the droplet losses mass\n"); + printf("1: The droplet surface does not move and the droplet mass is constant\n"); + return(NULL); + } + + ier=parseNumber(input, "dPdt" , MAXBUFLEN, &data->dPdt); + + ier=parseNumber(input, "Rg" , MAXBUFLEN, &data->Rg); + if(data->Rg < 0.0){ + printf("Rg must be greater than 0"); + return(NULL); + } + + ier=parseNumber (input, "reflectProblem" , MAXBUFLEN, &data->reflectProblem); + if(data->reflectProblem!=0 && data->reflectProblem!=1){ + printf("Invalid entry for reflectProblem! Can be only 1 or 0.\n"); + return(NULL); + } + + ier=parseNumber(input, "mdot" , MAXBUFLEN, &data->mdot); + + ier=parseNumber(input, "initialTemperature", MAXBUFLEN, + &data->initialTemperature); + if(ier==-1 || data->initialTemperature<=0.0e0){ + printf("Enter positive initialTemperature in K!\n"); + return(NULL); + } + + + ier=parseNumber(input, "initialPressure", MAXBUFLEN, + &data->initialPressure); + if(ier==-1 || data->initialTemperature<=0.0e0){ + printf("Enter positive initialPressure in atm!\n"); + return(NULL); + } + + ier=parseNumber (input, "metric" , MAXBUFLEN, &data->metric); + if(data->metric!=0 && data->metric!=1 && data->metric!=2){ + printf("Invalid entry for metric!\n"); + printf("0: Cartesian\n"); + printf("1: Cylindrical\n"); + printf("2: Spherical\n"); + return(NULL); + } + + ier=parseNumber(input, "QDot", MAXBUFLEN, &data->maxQDot); + if(ier==-1 && data->problemType==0){ + printf("Need to specify QDot for problemType 0!\n"); + return(NULL); + } + + ier=parseNumber(input, "kernelSize", MAXBUFLEN, &data->kernelSize); + if(ier==-1 && data->problemType==0){ + printf("Need to specify kernelSize for problemType 0!\n"); + return(NULL); + } + + ier=parseNumber(input, "ignTime", MAXBUFLEN, &data->ignTime); + if(ier==-1 && data->problemType==0){ + printf("Need to specify ignTime for problemType 0!\n"); + return(NULL); + } + + ier=parseNumber(input, "mixingWidth", MAXBUFLEN, + &data->mixingWidth); + if(ier==-1){ + printf("Need to specify mixingWidth!\n"); + return(NULL); + } + + ier=parseNumber(input, "shift", MAXBUFLEN, &data->shift); + + ier=parseNumber(input, "firstRadius", MAXBUFLEN, &data->firstRadius); + + ier=parseNumber(input, "wallTemperature", MAXBUFLEN, &data->wallTemperature); + + ier=parseNumber (input, "dirichletInner" , MAXBUFLEN, + &data->dirichletInner); + if(data->dirichletInner!=0 && data->dirichletInner!=1){ + printf("dirichletInner can either be 0 or 1!\n"); + return(NULL); + } + + ier=parseNumber (input, "dirichletOuter" , MAXBUFLEN, + &data->dirichletOuter); + if(data->dirichletOuter!=0 && data->dirichletOuter!=1){ + printf("dirichletOuter can either be 0 or 1!\n"); + return(NULL); + } + + ier=parseNumber (input, "adaptiveGrid" , MAXBUFLEN, + &data->adaptiveGrid); + if(ier==-1 || (data->adaptiveGrid!=0 && data->adaptiveGrid!=1)){ + printf("specify adaptiveGrid as 0 or 1!\n"); + return(NULL); + } + + ier=parseNumber (input, "moveGrid" , MAXBUFLEN, + &data->moveGrid); + if(ier==-1 || (data->moveGrid!=0 && data->moveGrid!=1)){ + printf("specify moveGrid as 0 or 1!\n"); + return(NULL); + } + + ier=parseNumber (input, "isotherm" , MAXBUFLEN, + &data->isotherm); + if(ier==-1){ + printf("specify temperature of isotherm!\n"); + return(NULL); + } + + ier=parseNumber(input, "gridOffset", MAXBUFLEN, &data->gridOffset); + + ier=parseNumber (input, "nSaves" , MAXBUFLEN, &data->nSaves); + if(data->nSaves<0 ){ + printf("nSaves must be greater than 0!\n"); + return(NULL); + } + + ier=parseNumber (input, "writeRates" , MAXBUFLEN, + &data->writeRates); + if(data->writeRates!=0 && data->writeRates!=1){ + printf("writeRates must either be 0 or 1!\n"); + return(NULL); + } + + ier=parseNumber (input, "writeEveryRegrid", MAXBUFLEN, + &data->writeEveryRegrid); + if(data->writeEveryRegrid!=0 && data->writeEveryRegrid!=1){ + printf("writeEveryRegrid must either be 0 or 1!\n"); + return(NULL); + } + + ier=parseNumber (input, "setConstraints" , MAXBUFLEN, + &data->setConstraints); + if(data->setConstraints!=0 && data->setConstraints!=1){ + printf("setConstraints must either be 0 or 1!\n"); + return(NULL); + } + + ier=parseNumber (input, "suppressAlg" , MAXBUFLEN, + &data->suppressAlg); + if(data->setConstraints!=0 && data->suppressAlg!=1){ + printf("suppressAlg must either be 0 or 1!\n"); + return(NULL); + } + + ier=parseNumber (input, "dryRun" , MAXBUFLEN, + &data->dryRun); + if(data->dryRun!=0 && data->dryRun!=1){ + printf("dryRun must either be 0 or 1!\n"); + return(NULL); + } + + ier=parseNumber (input, "finalTime" , MAXBUFLEN, + &data->finalTime); + + ier=parseNumber (input, "relativeTolerance" , MAXBUFLEN, + &data->relativeTolerance); + ier=parseNumber (input, "radiusTolerance" , MAXBUFLEN, + &data->radiusTolerance); + ier=parseNumber (input, "temperatureTolerance", MAXBUFLEN, + &data->temperatureTolerance); + ier=parseNumber (input, "pressureTolerance", MAXBUFLEN, + &data->pressureTolerance); + ier=parseNumber (input, "massFractionTolerance", MAXBUFLEN, + &data->massFractionTolerance); + ier=parseNumber (input, "bathGasTolerance", MAXBUFLEN, + &data->bathGasTolerance); + ier=parseNumber (input, "MdotTolerance", MAXBUFLEN, + &data->MdotTolerance); + + char chem[MAXBUFLEN],mix[MAXBUFLEN],tran[MAXBUFLEN]; + + ier=parseNumber(input, "chemistryFile" , MAXBUFLEN, chem); + if(ier==-1){ + printf("Enter chemistryFile!\n"); + return(NULL); + }else{ + try{ + data->gas = new Cantera::IdealGasMix(chem); + data->nsp=data->gas->nSpecies(); //assign no: of species + + } catch (Cantera::CanteraError& err) { + printf("Error:\n"); + printf("%s\n",err.what()); + return(NULL); + } + } + + ier=parseNumber(input, "transportModel", MAXBUFLEN, tran); + if(ier==-1){ + printf("Enter transportModel!\n"); + return(NULL); + }else{ + try{ + data->trmix = Cantera::newTransportMgr(tran, data->gas); + }catch (Cantera::CanteraError& err) { + printf("Error:\n"); + printf("%s\n",err.what()); + return(NULL); + } + } + + ier=parseNumber(input, "mixtureComposition", MAXBUFLEN, mix); + if(ier==-1){ + printf("Enter mixtureComposition!\n"); + return(NULL); + }else{ + if(data->gas!=NULL){ + try{ + data->gas->setState_TPX(data->initialTemperature, + data->initialPressure*Cantera::OneAtm, + mix); + }catch (Cantera::CanteraError& err) { + printf("Error:\n"); + printf("%s\n",err.what()); + return(NULL); + } + } + } + + ier=parseNumber(input, "dropletComposition", MAXBUFLEN, data->dropSpec); + if(ier==-1){ + printf("Enter composition of droplet!\n"); + return(NULL); + } + + + ier=parseNumber (input, "nThreads" , MAXBUFLEN, &data->nThreads); + if(data->nThreads<0 ){ + printf("nThreads must be greater than 0!\n"); + return(NULL); + } + + data->nr=0; + //data->np=data->nr+1; + data->nt=data->nr+1; + data->ny=data->nt+1; + data->np=data->ny+data->nsp; + data->nm=data->np+1; + + data->nvar=data->nsp+4; //assign no: of variables (R,T,P,Mdot,nsp species) + + //Mass of droplet + data->massDrop=1.0/3.0*pow(data->Rd,3)*997.0; //TODO: The density of the droplet should be a user input + + if(!data->adaptiveGrid){ + data->uniformGrid = new double [data->npts]; + data->neq=data->nvar*data->npts; + } + else{ + data->grid=(UserGrid) malloc(sizeof *data->grid); + ier=getGridSettings(input,data->grid); + if(ier==-1)return(NULL); + + ier=initializeGrid(data->grid); + if(ier==-1)return(NULL); + + ier=reGrid(data->grid, data->grid->position); + if(ier==-1)return(NULL); + + data->npts=data->grid->nPts; + data->neq=data->nvar*data->npts; + } + + data->output=fopen("output.dat","w"); + data->globalOutput=fopen("globalOutput.dat","w"); + data->gridOutput=fopen("grid.dat","w"); + //data->ratesOutput=fopen("rates.dat","w"); + + data->innerMassFractions = new double [data->nsp]; + + return(data); +} + +void setSaneDefaults(UserData data){ + data->domainLength=1.0e-02; + data->Rd=100.0e-06; + data->constantPressure=1; + data->problemType=1; + data->quasiSteady=1; + data->dPdt=0.0e0; + data->reflectProblem=0; + data->mdot=0.0; + data->initialTemperature=300.0; + data->initialPressure=1.0; + data->metric=0; + data->ignTime=1e-09; + data->maxQDot=0.0e0; + data->maxTemperature=300.0e0; + data->mixingWidth=1e-04; + data->shift=3.0e-03; + data->firstRadius=1e-04; + data->wallTemperature=298.0e0; + data->dirichletInner=0; + data->dirichletOuter=0; + data->adaptiveGrid=0; + data->moveGrid=0; + data->gridOffset=0.0e0; + data->Rg=1.0; + data->isotherm=1000.0; + data->nSaves=30; + data->writeRates=0; + data->writeEveryRegrid=0; + data->relativeTolerance=1e-06; + data->radiusTolerance=1e-08; + data->temperatureTolerance=1e-06; + data->pressureTolerance=1e-06; + data->massFractionTolerance=1e-09; + data->bathGasTolerance=1e-06; + data->finalTime=1e-02; + data->tNow=0.0e0; + data->setConstraints=0; + data->suppressAlg=1; + data->regrid=0; + data->gridDirection=1; + data->dryRun=0; + data->nThreads=1; + + data->flamePosition[0]=0.0e0; + data->flamePosition[1]=0.0e0; + data->flameTime[0]=0.0e0; + data->flameTime[1]=0.0e0; + data->nTimeSteps=0; +} + diff --git a/UserData.h b/UserData.h new file mode 100644 index 0000000..c7b13a3 --- /dev/null +++ b/UserData.h @@ -0,0 +1,192 @@ +#ifndef CANTERA_DEF +#define CANTERA_DEF +#include +#include +#endif + +#include "gridRoutines.h" + +#ifndef USER_DEF +#define USER_DEF +typedef struct UserDataTag{ + /*An ideal gas object from Cantera. Contains thermodynamic and kinetic + * info of all species.*/ + Cantera::IdealGasMix* gas; + /*A Transport object from Cantera. Contains all transport info of all + * species.*/ + Cantera::Transport* trmix; + /* Droplet species composition */ + char dropSpec[MAXBUFLEN]; + /*Length of the domain (in meters):*/ + double domainLength; + /*Initial Droplet Radius (in meters)*/ + double Rd; + /*Droplet Mass*/ + double massDrop; + /*Mass of gas in domain (in kg):*/ + double mass; + /*Parameter that indicates the symmetry of the problem;*/ + /*metric=0:Planar*/ + /*metric=1:Cylindrical*/ + /*metric=2:Spherical*/ + int metric; + /*No: of species:*/ + size_t nsp; + /*No: of equations:*/ + size_t neq; + /*No: of variables:*/ + size_t nvar; + /*Pointer indices (see "macros.h" for aliases that use these):*/ + /*Pointer index for temperature:*/ + size_t nt; + /*Pointer index for species:*/ + size_t ny; + /*Pointer index for spatial coordinate:*/ + size_t nr; + /*Pointer index for pressure:*/ + size_t np; + /*Pointer index for mass flow rate:*/ + size_t nm; + /*Species index of bath gas:*/ + size_t k_bath; + /*Species index of oxidizer:*/ + size_t k_oxidizer; + size_t k_OH; + size_t k_HO2; + /*Species index of droplet composition*/ + size_t k_drop; + /*User-defined mass flux (kg/m^2/s):*/ + double mdot; + /*Flag to solve isobaric/isochoric problem;*/ + /*constantPressure=1: isobaric*/ + /*constantPressure=0: isochoric*/ + int constantPressure; + /*User-defined dPdt (Pa/s), activates when problem is "isobaric":*/ + double dPdt; + /*Initial temperature of the gas (K):*/ + double initialTemperature; + /*Initial Pressure of the gas (atm):*/ + double initialPressure; + /*Classification of problem type;*/ + /*problemType=0: Mixture is premixed and spatially uniform initially. + * In order for mixture to ignite, an external heat source (finite + * maxQDot) must be used.*/ + /*problemType=1: Mixture is premixed but spatially non-uniform + * initially. Equilibrium products are contained within a hot kernel of + * size given by "shift" and a mixing length scale given by + * "mixingWidth".*/ + /*problemType=2: User specified initial condition. Use file + * "initialCondition.dat".*/ + int problemType; + /*Quasi-Steady Assumption: + *quasiSteady=0: The droplet surface recedes and the droplet losses mass. + *quasiSteady=1: The droplet surface does not move and the droplet mass is constant.*/ + int quasiSteady; + /*Maximum External heat source (K/s):*/ + double maxQDot; + /*Ignition kernel size:*/ + double kernelSize; + + double maxTemperature; + /*Maximum time for which the external heat source is applied (s):*/ + double ignTime; + /*Vector of Mass Fractions used to impose Robin Boundary Condition for + * species at the domain origin:*/ + double* innerMassFractions; + /*Value of temperature to be used if Dirichlet Boundary Conditions are + * imposed for temperature:*/ + double innerTemperature; + double wallTemperature; + /*Isotherm chosen to find the location of a "burning" front (K):*/ + double isotherm; + /*Interval of time integration:*/ + double finalTime; + /*Current time:*/ + double tNow; + /*Flag to reflect initial conditions across center of the domain:*/ + int reflectProblem; + /*Parameters for initial conditions in setting up profiles: + increasing function of x: g=0.5*(erf(x-3*w-shift)/w)+1) + decreasing function of x: f=1-g*/ + double mixingWidth; + double shift; + double firstRadius; + /*Flag to run program without time-integration i.e. simply layout the + * initial conditions and quit:*/ + int dryRun; + /*Relative Tolerance:*/ + double relativeTolerance; + /*Absolute Tolerance for spatial coordinate:*/ + double radiusTolerance; + /*Absolute Tolerance for Temperature:*/ + double temperatureTolerance; + /*Absolute Tolerance for Pressure:*/ + double pressureTolerance; + /*Absolute Tolerance for Mass Fractions:*/ + double massFractionTolerance; + /*Absolute Tolerance for bath gas mass fraction:*/ + double bathGasTolerance; + /*Absolute Tolerance for Mdot:*/ + double MdotTolerance; + /*Flag to set constraints on Mass fractions so they don't acquire + * negative values:*/ + int setConstraints; + /*Flag to suppress error checking on algebraic variables:*/ + int suppressAlg; + /*Number of time-steps elapsed before saving the solution:*/ + int nSaves; + /*Flag to set write for every regrid:*/ + int writeEveryRegrid; + /*Solution output file:*/ + FILE* output; + /*Flag to write the rates (ydot) of solution components into the + * "ratesOutput" file:*/ + int writeRates; + /*Grid output file:*/ + FILE* gridOutput; + ///*Rate of change (ydot) output file (see "writeRates"):*/ + //FILE* ratesOutput; + /*Global properties (mdot, radius of flame, etc.) output file:*/ + FILE* globalOutput; + + /*Flag to adapt grid:*/ + int adaptiveGrid; + /*Flag to move grid:*/ + int moveGrid; + /*Flag to initiate regrid:*/ + int regrid; + /*Integer that specifies grid movement direction: + * gridDirection = -1: Move Left + * gridDirection = +1: Move Right*/ + int gridDirection; + + /*Grid Ratio: This replaces the uniform grid. dX0 and dXf are the grid + spacing at the droplet surface and right boundary, respectivly. The Grid + Ratio is equal to dXf/dX0. A Rg>1 focuses grid points on the droplet + surface while a Rg<1 focuses grid points at the right boundary. A Rg of 1 + is a uniform grid.*/ + double Rg; + /*Total number of points for grid:*/ + size_t npts; + + double gridOffset; + + UserGrid grid; + double* uniformGrid; + + int dirichletInner,dirichletOuter; + + int nThreads; + double clockStart; + + /*These arrays are used to compute dr/dt, which in turn is used to + * compute the flame speed S_u:*/ + double flamePosition[2]; + double flameTime[2]; + size_t nTimeSteps; +} *UserData; +UserData allocateUserData(FILE *input); +void setSaneDefaults(UserData data); +void freeUserData(UserData data); +#endif + diff --git a/grid2/Makefile b/grid2/Makefile new file mode 100644 index 0000000..05fccae --- /dev/null +++ b/grid2/Makefile @@ -0,0 +1,47 @@ +#compiler =icpc +EXE =gridTest + +compiler =g++ + +GSL_INCLUDES =-I/usr/include/gsl +GSL_LIBS =-L/usr/lib -lgsl -lgslcblas +RM=rm -f + +compiler?=g++ + +ifeq ($(compiler),g++) + CPPFLAGS= -Wall -O3 + CPP=g++ +endif + +ifeq ($(compiler),icpc) + CPPFLAGS= -Wall -O3 -gxx-name=/usr/bin/g++-7 -std=c++11 + CPP=icpc +endif + +OBJS = parse.o gridRoutines.o main.o + +all: $(EXE) + +# pull in dependency info for *existing* .o files +-include $(OBJS:.o=.d) + +parse.o: parse.cpp parse.h parse.hpp + $(CPP) $(CPPFLAGS) -c parse.cpp -o parse.o + $(CPP) -MM parse.cpp > parse.d + +gridRoutines.o: gridRoutines.cpp gridRoutines.h parse.h + $(CPP) $(CPPFLAGS) -c gridRoutines.cpp -o gridRoutines.o + $(CPP) -MM gridRoutines.cpp > gridRoutines.d + +main.o: main.cpp gridRoutines.cpp gridRoutines.h parse.h + $(CPP) $(CPPFLAGS) -c main.cpp -o main.o + $(CPP) -MM main.cpp > main.d + +$(EXE): $(OBJS) + $(CPP) $(CPPFLAGS) $(GSL_INCLUDES) $(OBJS) -o $(EXE) $(GSL_LIBS) + +.PHONY: clean +clean: + rm -f $(EXE) $(OBJS) *.d + diff --git a/grid2/gridRoutines.cpp b/grid2/gridRoutines.cpp new file mode 100644 index 0000000..f4a9065 --- /dev/null +++ b/grid2/gridRoutines.cpp @@ -0,0 +1,244 @@ +#include "gridRoutines.h" +#include +inline double l(const double* x, + const double* a, + const double* w, + const double* fac, + const int* refineLeft){ + if(*refineLeft==0){ + return(tanh(-*a*(*x+*w*100.0e0))); + }else{ + return(tanh(-*a*(*x-*w*(*fac)))); + } +} + +inline double r(const double* x, + const double* a, + const double* w, + const double* fac, + const int* refineRight){ + if(*refineRight==0){ + return(tanh(*a*(*x-(1.0e0+*w*100.0e0)))); + }else{ + return(tanh(*a*(*x-(1.0e0-*w*(*fac))))); + } +} + +inline double f(const double* x, + const double* a, + const double* c, + const double* w){ + return(tanh(-*a*(*x-(*c+*w))) + +tanh(-*a*((*x-1.0e0)-(*c+*w))) + +tanh(-*a*((*x+1.0e0)-(*c+*w)))); +} + +inline double g(const double* x, + const double* a, + const double* c, + const double* w){ + return(tanh(*a*(*x-(*c-*w))) + +tanh(*a*((*x-1.0e0)-(*c-*w))) + +tanh(*a*((*x+1.0e0)-(*c-*w)))); +} + +inline double rho(const double* x, + const double* a, + const double* c, + const double* w, + const double* mag, + const double* leftFac, + const double* rightFac, + const int* refineLeft, + const int* refineRight){ + + return(((2.0e0+f(x,a,c,w) + +g(x,a,c,w) + +l(x,a,w,leftFac,refineLeft) + +r(x,a,w,rightFac,refineRight))*0.5e0) + *(*mag-1.0e0)+1.0e0); +} + +size_t maxPoints(const size_t basePts, + const double* a, + const double* w, + const double* mag, + const double* leftFac, + const double* rightFac, + const int* refineLeft, + const int* refineRight){ + double dx=1.0e0/((double)(basePts)-1.0e0); + double y=0.0e0; + size_t i=0; + double r=0.0e0; + double t=0.5e0; + while(y<=1.0e0){ + r=rho(&y,a,&t,w,mag,leftFac,rightFac,refineLeft,refineRight); + y=y+(dx/r); + i++; + } + return(i); +} + +void fillGrid(const size_t* basePts, + const size_t* nPts, + const double* a, + const double* c, + const double* w, + const double* mag, + const double* leftFac, + const double* rightFac, + const int* refineLeft, + const int* refineRight, + double x[]){ + + FILE* out;out=fopen("tmp.dat","w"); + + double y=0.0e0; + double r=0.0e0; + double dx=1.0e0/((double)(*basePts)-1.0e0); + for(size_t j=0;j<*nPts;j++){ + r=rho(&y,a,c,w,mag,leftFac,rightFac,refineLeft,refineRight); + fprintf(out, "%15.15e\n",dx/r); + y=y+(dx/r); + } + fclose(out); + + double dxp[*nPts-1]; + for (size_t j = 0; j < *nPts; j++) { + dxp[j]=0.0e0; + } + + FILE* tmp;tmp=fopen("tmp.dat","r"); + char buf[MAXBUFLEN]; + size_t i=0; + while (fgets(buf,MAXBUFLEN, tmp)!=NULL){ + sscanf(buf, "%lf", &y); + dxp[i]=y; + i++; + } + fclose(tmp); + + double sum=0.0e0; + double err=0.0e0; + double fix=0.0e0; + for(size_t j=0;j<*nPts-1;j++){ + sum+=dxp[j]; + } + err=1.0e0-sum; + printf("sum before correction: %15.6e\n",sum); + printf("err before correction: %15.6e\n",err); + fix=err/((double)(*nPts)); + sum=0.0e0; + for(size_t j=0;j<*nPts-1;j++){ + dxp[j]+=fix; + sum+=dxp[j]; + } + err=1.0e0-sum; + printf("sum after correction:%15.6e\n",sum); + printf("err after correction: %15.6e\n",err); + + x[0]=0.0e0; + for(size_t j=0;j<*nPts-1;j++){ + x[j+1]=x[j]+dxp[j]; + } + x[*nPts-1]=1.0e0; + +} + +double safePosition(double c, double w){ + if(c1.0e0-w){ + return(1.0e0-w); + } + else{ + return(c); + } +} + +int reGrid(UserGrid grid, double position){ + + printf("before regrid: %ld\n", grid->nPts); + double xx[grid->nPts]; + + fillGrid(&grid->basePts, + &grid->nPts, + &grid->a, + &position, + &grid->w, + &grid->mag, + &grid->leftFac, + &grid->rightFac, + &grid->refineLeft, + &grid->refineRight, + xx); + + for (size_t i = 0; i < grid->nPts; i++) { + grid->x[i]=xx[i]; + } + return(0); +} + +void storeGrid(const double* x, double *y, const size_t nPts){ + for(size_t i=0;inPts=maxPoints(grid->basePts, + &grid->a, + &grid->w, + &grid->mag, + &grid->leftFac, + &grid->rightFac, + &grid->refineLeft, + &grid->refineRight); + printf("nPts: %ld\n",grid->nPts); + grid->leastMove=grid->w; + + grid->x = new double [grid->nPts]; + grid->xOld = new double [grid->nPts]; + for (size_t i = 0; i < grid->nPts; i++) { + grid->x[i]=0.0e0; + grid->xOld[i]=0.0e0; + } + return(0); +} + +int getGridSettings(FILE *input, UserGrid grid){ + + int ier=0; + + ier=parseNumber(input, "basePts" , MAXBUFLEN, &grid->basePts); + if(ier==-1)return(-1); + + ier=parseNumber(input, "gridDensitySlope", MAXBUFLEN, &grid->a); + if(ier==-1)return(-1); + + ier=parseNumber(input, "fineGridHalfWidth", MAXBUFLEN, &grid->w); + if(ier==-1)return(-1); + + ier=parseNumber(input, "gridRefinement", MAXBUFLEN, &grid->mag); + if(ier==-1)return(-1); + + ier=parseNumber(input, "leftRefineFactor", MAXBUFLEN, &grid->leftFac); + if(ier==-1)return(-1); + + ier=parseNumber(input, "rightRefineFactor", MAXBUFLEN, &grid->rightFac); + if(ier==-1)return(-1); + + ier=parseNumber(input, "refineLeft" , MAXBUFLEN, &grid->refineLeft); + if(ier==-1)return(-1); + + ier=parseNumber(input, "refineRight" , MAXBUFLEN, &grid->refineRight); + if(ier==-1)return(-1); + + ier=parseNumber(input, "position" , MAXBUFLEN, &grid->position); + if(ier==-1)return(-1); + + return(0); +} + diff --git a/grid2/gridRoutines.d b/grid2/gridRoutines.d new file mode 100644 index 0000000..80fc6ad --- /dev/null +++ b/grid2/gridRoutines.d @@ -0,0 +1 @@ +gridRoutines.o: gridRoutines.cpp gridRoutines.h parse.h parse.hpp diff --git a/grid2/gridRoutines.h b/grid2/gridRoutines.h new file mode 100644 index 0000000..06231ac --- /dev/null +++ b/grid2/gridRoutines.h @@ -0,0 +1,82 @@ +#include "parse.h" + +#ifndef GSL_DEF +#define GSL_DEF +#include +#include +#endif + +#ifndef GRID_DEF +#define GRID_DEF + +typedef struct gridTag{ + + size_t basePts; + size_t nPts; + double position; + double leastMove; + double a; + double w; + double mag; + double leftFac; + double rightFac; + int refineLeft; + int refineRight; + double* x; + double* xOld; + +} *UserGrid; + +inline double l(const double* x, + const double* a, + const double* w, + const double* fac, + const int* refineLeft); + +inline double r(const double* x, + const double* a, + const double* w, + const double* fac, + const int* refineRight); + +inline double f(const double* x, + const double* a, + const double* c, + const double* w); + +inline double g(const double* x, + const double* a, + const double* c, + const double* w); + +inline double rho(const double* x, + const double* a, + const double* c, + const double* w, + const double* mag, + const double* leftFac, + const double* rightFac, + const int* refineLeft, + const int* refineRight); + +size_t maxPoints(const size_t basePts, + const double* a, + const double* w, + const double* mag, + const double* leftFac, + const double* rightFac, + const int* refineLeft, + const int* refineRight); + + +double safePosition(double c, double w); + +int reGrid(UserGrid grid, double position); + +void storeGrid(const double* x, double *y, const size_t nPts); + +int initializeGrid(UserGrid grid); + +int getGridSettings(FILE *input, UserGrid grid); + +#endif diff --git a/grid2/gridRoutines.o b/grid2/gridRoutines.o new file mode 100644 index 0000000000000000000000000000000000000000..a86e877809368ab6358735fea0b89ab4ccfc609c GIT binary patch literal 13576 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 malloc(sizeof *data->grid); + FILE *input;input=fopen("input.dat","r"); + ier=getGridSettings(input,data->grid); + if(ier==-1)return(-1); + fclose(input); + + ier=initializeGrid(data->grid); + if(ier==-1)return(-1); + + ier=reGrid(data->grid, data->grid->position); + if(ier==-1)return(-1); + + FILE* output1;output1=fopen("grid.dat","w"); + for(size_t i=0;igrid->nPts;i++){ + if(i==0){ + fprintf(output1, "%ld\t%15.15e\t%15.15e\n",i,data->grid->x[i],data->grid->x[1]-data->grid->x[0]); + } + else{ + fprintf(output1, "%ld\t%15.15e\t%15.15e\n",i,data->grid->x[i],data->grid->x[i]-data->grid->x[i-1]); + } + } + fclose(output1); + + + if(data->grid->xOld!=NULL){ + delete[] data->grid->xOld; + printf("old grid array Deleted!\n"); + } + if(data->grid->x!=NULL){ + delete[] data->grid->x; + printf("current grid array Deleted!\n"); + } + if(data->grid!=NULL){ + free(data->grid); + printf("grid object Freed!\n"); + } + + return(0); +} diff --git a/grid2/main.d b/grid2/main.d new file mode 100644 index 0000000..d68c7f1 --- /dev/null +++ b/grid2/main.d @@ -0,0 +1 @@ +main.o: main.cpp gridRoutines.h parse.h parse.hpp diff --git a/grid2/main.o b/grid2/main.o new file mode 100644 index 0000000000000000000000000000000000000000..b59fddb69a8861340c4666555dedcf34c92200ce GIT binary patch literal 3424 zcmb`J&2Jk;6u_Trm%5={yOfVWp*WyGkjP@ENkdCPb`m=qWST${2%%xSvDc1U$F|n% zrim1_$P~H?K?sR^k@zDjA<;-FB^yL>+2;)7RUyT>WOmA%_~+*acd0tpN#lqxk-&T1wsK;9^1ZMCzQ-P;-K zlVg24Z`+_;%tpu~q8Wy^7#Y%wx~XS7AX768y<~dYZ2$7?89if0Qie|MP4Z{x@ZX8a zYhi?miOF%L{!8!i9c8tTn6mn^-B^Cb`r0vAzbe)@Bi41hSKr(Uz5fi( zK5hNP%)f0L_M_omD4Yx{mO%fq^NQ(Xx18AeauCw1-83Q&P>bI+LQlrwOIloIB{`;8 zKc=mpzoISbD^${d!bY5o10g6kym})=cGC|6Fk?eT)I`eL6Na(3=9UOgSd8~znO_Qyf?HyZF>eqws%p^`FX4dN)TX%nl z;laV+vTFI$*lXm>1Hv%5XdIRNJZ0W9>Ac4HYnISDwyHDZ1&Bo8_b_?F%P z-}$ib(7rvvPjGQgAb*_MF4AJ9r10vlVX6Hie?p3U5J*T}_1!~K)UWy4q%K4WseM2S z4@kkp{VdG0p;Q6*W6R6v_dEggtn^xTioY?7W}Dxhbu*)$-(CeT9^46Sf8p>69)4&O zdXDN4TG?g^+1LQOkGS~Lj6d( zfjS-Q%j@PaUfkolX%e!3w$f%kcNhinp>4Kin@{r3E%T=Acdkdw4SI$l-0jeJ{|CehAAyTaI*~PU7ayegk`- z(^=sDFWBA=r;CB}daDf5e!5#YKOy)(A@ElP zF6Mb%$V2Cq=Xq1`7xTO;aJq}Qzb@oCAn-GSznCY2J|y(~_t_e&|TH-Ua{G>~VjRgjG`b;vU&<8d?eq9&6Q63sAccos9|`FZ@c0l8E*bs0Z=-Dt|3 zW&M1qCRYqRWrn%P!u2^dXK3@d^Zy^AcT7vrG)L^mv3Lnvu7;@tHDdfb%?)v`xLSAo zvuF=u`LFTxJ)xX*|BDi5ovH{?=i~F1{784te+?PQMegWycYHHmLQZET&g{Io!V*r= z2121TO#e1CR}wlyBqy=Z5K-y~5@&Ip0wa|_4sYKQnCPmrc#)CL<2cWwLSIw2`y2xi z?#NpH{2X6Eg>t&%FEcR39a-y+UqbG+ew)mZuM@2w-Pb&xbFZ;!sM~D(JFKO<@}NgN k{W-b!9wOMFmxc0!(==-L`q5qD6(455WL6{6QQYzW0{O`lzW@LL literal 0 HcmV?d00001 diff --git a/grid2/parse.cpp b/grid2/parse.cpp new file mode 100644 index 0000000..f872ca5 --- /dev/null +++ b/grid2/parse.cpp @@ -0,0 +1,20 @@ +#include "parse.h" +void getFromString (const char* buf, int* n){ + *n=atoi(buf); + printf("%d\n",*n); +} + +void getFromString (const char* buf, size_t* n){ + *n=(size_t)(atoi(buf)); + printf("%lu\n",*n); +} + +void getFromString (const char* buf, double* n){ + *n=(double)(atof(buf)); + printf("%15.6e\n",*n); +} + +void getFromString (const char* buf, char* n){ + sscanf(buf,"%s",n); + printf("%s\n",n); +} diff --git a/grid2/parse.d b/grid2/parse.d new file mode 100644 index 0000000..7abe434 --- /dev/null +++ b/grid2/parse.d @@ -0,0 +1 @@ +parse.o: parse.cpp parse.h parse.hpp diff --git a/grid2/parse.h b/grid2/parse.h new file mode 100644 index 0000000..45f3a2c --- /dev/null +++ b/grid2/parse.h @@ -0,0 +1,31 @@ +#ifndef PRINT_DEF +#define PRINT_DEF +#include //for strings +#include //for printf,scanf +#include //for atoi, atof +#endif + +#ifndef PARSE_DEF +#define PARSE_DEF +#define MAXBUFLEN 200 + +void getFromString (const char* buf, int* n); +void getFromString (const char* buf, size_t* n); +void getFromString (const char* buf, double* n); +void getFromString (const char* buf, char* n); + +int parseString(FILE* input, const char* keyword, const size_t bufLen, char* n); + +template +int parseNumber(FILE* input, const char* keyword, const size_t bufLen, T* n); + +template +int parseArray(FILE* input, const char* keyword, const size_t bufLen, + const size_t arrLen, T y[]); + + +#include "parse.hpp" + +#endif + + diff --git a/grid2/parse.hpp b/grid2/parse.hpp new file mode 100644 index 0000000..634db96 --- /dev/null +++ b/grid2/parse.hpp @@ -0,0 +1,76 @@ +template +int parseNumber(FILE* input, const char* keyword, const size_t bufLen, T* n){ + + char buf[bufLen]; + char buf1[bufLen]; + char comment[1]; + char *ret; + + while (fgets(buf,bufLen, input)!=NULL){ + comment[0]=buf[0]; + if(strncmp(comment,"#",1)==0){ + //printf("Comment!:%s\n",buf); + } + else{ + ret=strtok(buf,"="); + if(strcmp(ret,keyword)==0){ + /*offset buf by keyword size + 1 for the "="*/ + strncpy (buf1, buf+strlen(keyword)+1, bufLen); + printf("%30s: ",keyword); + getFromString(buf1,n); + rewind(input); + return(0); + } + } + } + rewind(input); + return(-1); +} + +template +int parseArray(FILE* input, const char* keyword, const size_t bufLen, + const size_t arrLen, T y[]){ + + char buf[bufLen]; + char buf1[bufLen]; + char comment[1]; + char *ret; + + while (fgets(buf,bufLen, input)!=NULL){ + comment[0]=buf[0]; + if(strncmp(comment,"#",1)==0){ + //printf("Comment!:%s\n",buf); + } + else{ + ret=strtok(buf,"="); + + if(strcmp(ret,keyword)==0){ + /*offset buf by keyword size + 1 for the "="*/ + strncpy (buf1, buf+strlen(keyword)+1, bufLen); + printf("%30s:\n",keyword); + ret=strtok(buf1,","); + size_t j=0; + while(ret!=NULL){ + if(j3d)LCO-I7N}*~0f^Lr1IVr$cW^%SP!kLRHNM4nc* z*!ug1z5WcOZ{V%|MeiSbz1TW*Lj^3Xsr*~1RnFg#STC~CcOa{M`v$%sm4B|-+9;)iNI|5oK3tn|>}YLh$roHgpR4vD75$1mhg&CNG!Q8PDYjad01JKt2(;nOj| z3lQz%*3WG>x2dn^sEcSFEXxgYI98PT{u|2N>d&0m-@SK@pZ6H^liD@}J<;2LugmS- z%R|`CgRxlXl&#XX&4RIfhECbaZCeA3b?7~jT(!L$=14Xd=+XZKWQ^8r7azssb@5ppq^8=={9%soY zcFhJ@A2jwyoguSPZt>dMQL9o1KGT7hnInImHAK|`>zmxb%d8=)23Y^ZEp6SOnZtjL zHAK|~>xbOZ{Qu~{so@5pZ+VRdS*7tjIvd?Ex)E}&+ars9G#S*ZGf}WyTb%yNn_ebo zs&wV@rIK@Ha&l&3)|tI@xisNW7zI&%l^mzRp3#EiEv=*{sHWpeoS~9#^*HfzSog-p zoG|p<+5$D25r5LL$UDn-``?>bj%qu9zp6Hb7c@`$PZ|EW?Zkh@@W;0={Ep#|nic+w z!S@^dSIx2RM+QG_aMY^gx!!@_@4z1!9Pci9a++fw&_q7h1L1i+znb7>J&zJx>W{4O zb~SS6S#uRAy(FW+Uv;g>zZH>HaU++k`7k6asAB?m+v1Y*>eZ@WW0zVz@-4n{hr-D9 zR`h(a*0dTyz2OJZZ4O03f5}-0+^Vl1=si`A<8fyuZ-fFf$m3YGd#N`8CwPD}EeO#1 z2_Q=j$yrb%7|KnEe*)xFZs5rqcmu{1(dpWjvpho1ca66>7CDExnIh}U|4wYOzUTrQ zFqZlezdhPvP=MAFC;Cg~GB#b$q`?&5Ys~Q1F>4!n@;W|;i6OH8g1+McT$AzBI{tw; zYMqQ +inline double l(const double* x, + const double* a, + const double* w, + const double* fac, + const int* refineLeft){ + if(*refineLeft==0){ + return(tanh(-*a*(*x+*w*100.0e0))); + }else{ + return(tanh(-*a*(*x-*w*(*fac)))); + } +} + +inline double r(const double* x, + const double* a, + const double* w, + const double* fac, + const int* refineRight){ + if(*refineRight==0){ + return(tanh(*a*(*x-(1.0e0+*w*100.0e0)))); + }else{ + return(tanh(*a*(*x-(1.0e0-*w*(*fac))))); + } +} + +inline double f(const double* x, + const double* a, + const double* c, + const double* w){ + return(tanh(-*a*(*x-(*c+*w))) + +tanh(-*a*((*x-1.0e0)-(*c+*w))) + +tanh(-*a*((*x+1.0e0)-(*c+*w)))); +} + +inline double g(const double* x, + const double* a, + const double* c, + const double* w){ + return(tanh(*a*(*x-(*c-*w))) + +tanh(*a*((*x-1.0e0)-(*c-*w))) + +tanh(*a*((*x+1.0e0)-(*c-*w)))); +} + +inline double rho(const double* x, + const double* a, + const double* c, + const double* w, + const double* mag, + const double* leftFac, + const double* rightFac, + const int* refineLeft, + const int* refineRight){ + + return(((2.0e0+f(x,a,c,w) + +g(x,a,c,w) + +l(x,a,w,leftFac,refineLeft) + +r(x,a,w,rightFac,refineRight))*0.5e0) + *(*mag-1.0e0)+1.0e0); +} + +size_t maxPoints(const size_t basePts, + const double* a, + const double* w, + const double* mag, + const double* leftFac, + const double* rightFac, + const int* refineLeft, + const int* refineRight){ + double dx=1.0e0/((double)(basePts)-1.0e0); + double y=0.0e0; + size_t i=0; + double r=0.0e0; + double t=0.5e0; + while(y<=1.0e0){ + r=rho(&y,a,&t,w,mag,leftFac,rightFac,refineLeft,refineRight); + y=y+(dx/r); + i++; + } + return(i); +} + +void fillGrid(const size_t* basePts, + const size_t* nPts, + const double* a, + const double* c, + const double* w, + const double* mag, + const double* leftFac, + const double* rightFac, + const int* refineLeft, + const int* refineRight, + double x[]){ + + FILE* out;out=fopen("tmp.dat","w"); + + double y=0.0e0; + double r=0.0e0; + double dx=1.0e0/((double)(*basePts)-1.0e0); + for(size_t j=0;j<*nPts;j++){ + r=rho(&y,a,c,w,mag,leftFac,rightFac,refineLeft,refineRight); // Point density? + fprintf(out, "%15.15e\n",dx/r); // writing number of points per section to tmp.dat + y=y+(dx/r); // y is the total number of points? + } + fclose(out); + + double dxp[*nPts-1]; + for (size_t j = 0; j < *nPts; j++) { + dxp[j]=0.0e0; + } + + FILE* tmp;tmp=fopen("tmp.dat","r"); + char buf[MAXBUFLEN]; + size_t i=0; + while (fgets(buf,MAXBUFLEN, tmp)!=NULL){ + sscanf(buf, "%lf", &y); + dxp[i]=y; + i++; + } + fclose(tmp); + + double sum=0.0e0; + double err=0.0e0; + double fix=0.0e0; + for(size_t j=0;j<*nPts-1;j++){ + sum+=dxp[j]; + } + err=1.0e0-sum; + printf("sum before correction: %15.6e\n",sum); + printf("err before correction: %15.6e\n",err); + fix=err/((double)(*nPts)); + sum=0.0e0; + for(size_t j=0;j<*nPts-1;j++){ + dxp[j]+=fix; + sum+=dxp[j]; + } + err=1.0e0-sum; + printf("sum after correction:%15.6e\n",sum); + printf("err after correction: %15.6e\n",err); + + x[0]=0.0e0; + for(size_t j=0;j<*nPts-1;j++){ + x[j+1]=x[j]+dxp[j]; + } + x[*nPts-1]=1.0e0; + +} + +double safePosition(double c, double w){ + if(c1.0e0-w){ + return(1.0e0-w); + } + else{ + return(c); + } +} + +int reGrid(UserGrid grid, double position){ + + printf("before regrid: %ld\n", grid->nPts); + double xx[grid->nPts]; + + fillGrid(&grid->basePts, + &grid->nPts, + &grid->a, + &position, + &grid->w, + &grid->mag, + &grid->leftFac, + &grid->rightFac, + &grid->refineLeft, + &grid->refineRight, + xx); + + for (size_t i = 0; i < grid->nPts; i++) { + grid->x[i]=xx[i]; + } + return(0); +} + +void storeGrid(const double* x, double *y, const size_t nPts){ + for(size_t i=0;inPts=maxPoints(grid->basePts, + &grid->a, + &grid->w, + &grid->mag, + &grid->leftFac, + &grid->rightFac, + &grid->refineLeft, + &grid->refineRight); + printf("nPts: %ld\n",grid->nPts); + grid->leastMove=grid->w; + + grid->x = new double [grid->nPts]; + grid->xOld = new double [grid->nPts]; + for (size_t i = 0; i < grid->nPts; i++) { + grid->x[i]=0.0e0; + grid->xOld[i]=0.0e0; + } + return(0); +} + +int getGridSettings(FILE *input, UserGrid grid){ + + int ier=0; + + ier=parseNumber(input, "basePts" , MAXBUFLEN, &grid->basePts); + if(ier==-1)return(-1); + + ier=parseNumber(input, "gridDensitySlope", MAXBUFLEN, &grid->a); + if(ier==-1)return(-1); + + ier=parseNumber(input, "fineGridHalfWidth", MAXBUFLEN, &grid->w); + if(ier==-1)return(-1); + + ier=parseNumber(input, "gridRefinement", MAXBUFLEN, &grid->mag); + if(ier==-1)return(-1); + + ier=parseNumber(input, "leftRefineFactor", MAXBUFLEN, &grid->leftFac); + if(ier==-1)return(-1); + + ier=parseNumber(input, "rightRefineFactor", MAXBUFLEN, &grid->rightFac); + if(ier==-1)return(-1); + + ier=parseNumber(input, "refineLeft" , MAXBUFLEN, &grid->refineLeft); + if(ier==-1)return(-1); + + ier=parseNumber(input, "refineRight" , MAXBUFLEN, &grid->refineRight); + if(ier==-1)return(-1); + + ier=parseNumber(input, "position" , MAXBUFLEN, &grid->position); + if(ier==-1)return(-1); + + return(0); +} + diff --git a/gridRoutines.h b/gridRoutines.h new file mode 100644 index 0000000..06231ac --- /dev/null +++ b/gridRoutines.h @@ -0,0 +1,82 @@ +#include "parse.h" + +#ifndef GSL_DEF +#define GSL_DEF +#include +#include +#endif + +#ifndef GRID_DEF +#define GRID_DEF + +typedef struct gridTag{ + + size_t basePts; + size_t nPts; + double position; + double leastMove; + double a; + double w; + double mag; + double leftFac; + double rightFac; + int refineLeft; + int refineRight; + double* x; + double* xOld; + +} *UserGrid; + +inline double l(const double* x, + const double* a, + const double* w, + const double* fac, + const int* refineLeft); + +inline double r(const double* x, + const double* a, + const double* w, + const double* fac, + const int* refineRight); + +inline double f(const double* x, + const double* a, + const double* c, + const double* w); + +inline double g(const double* x, + const double* a, + const double* c, + const double* w); + +inline double rho(const double* x, + const double* a, + const double* c, + const double* w, + const double* mag, + const double* leftFac, + const double* rightFac, + const int* refineLeft, + const int* refineRight); + +size_t maxPoints(const size_t basePts, + const double* a, + const double* w, + const double* mag, + const double* leftFac, + const double* rightFac, + const int* refineLeft, + const int* refineRight); + + +double safePosition(double c, double w); + +int reGrid(UserGrid grid, double position); + +void storeGrid(const double* x, double *y, const size_t nPts); + +int initializeGrid(UserGrid grid); + +int getGridSettings(FILE *input, UserGrid grid); + +#endif diff --git a/lagrangianCombustion2 b/lagrangianCombustion2 new file mode 100755 index 0000000000000000000000000000000000000000..78125e7e9f047b8ed7f1cc118d3349980b1a33b0 GIT binary patch literal 264040 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
    !#2?kq4Si)vNiMgHV9 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 Yt;~?wcyP*p>AMp8F~ulnvOQ}558-1PF#rGn literal 0 HcmV?d00001 diff --git a/macros.h b/macros.h new file mode 100644 index 0000000..b70d477 --- /dev/null +++ b/macros.h @@ -0,0 +1,69 @@ +#ifndef MACRO_DEF +#define MACRO_DEF +//#define SUNDIALS_DOUBLE_PRECISION 1 +//#define SUNDIALS_SINGLE_PRECISION 1 + +#define ZERO RCONST(0.0) +#define HALF RCONST(0.5) +#define ONE RCONST(1.0) +#define TWO RCONST(2.0) +#define THREE RCONST(3.0) +#define FOUR RCONST(4.0) +#define TEN RCONST(10.0) + +/* In order to keep begin the index numbers from 1 instead of 0, we define + * macros here. Also, we define macros to ease referencing various variables in + * the sundials nvector. + */ +#define psi(i) psidata[i-1] + +#define T(i) ydata[((i-1)*data->nvar)+data->nt] +#define Y(i,k) ydata[((i-1)*data->nvar)+data->ny+k-1] +#define R(i) ydata[((i-1)*data->nvar)+data->nr] +#define P(i) ydata[((i-1)*data->nvar)+data->np] +#define Mdot(i) ydata[((i-1)*data->nvar)+data->nm] + +#define Tdot(i) ydotdata[((i-1)*data->nvar)+data->nt] +#define Ydot(i,k) ydotdata[((i-1)*data->nvar)+data->ny+k-1] +#define Rdot(i) ydotdata[((i-1)*data->nvar)+data->nr] +#define Pdot(i) ydotdata[((i-1)*data->nvar)+data->np] +#define Mdotdot(i) ydotdata[((i-1)*data->nvar)+data->nm] + +#define Tres(i) resdata[((i-1)*data->nvar)+data->nt] +#define Yres(i,k) resdata[((i-1)*data->nvar)+data->ny+k-1] +#define Rres(i) resdata[((i-1)*data->nvar)+data->nr] +#define Pres(i) resdata[((i-1)*data->nvar)+data->np] +#define Mdotres(i) resdata[((i-1)*data->nvar)+data->nm] + +#define Tid(i) iddata[((i-1)*data->nvar)+data->nt] +#define Yid(i,k) iddata[((i-1)*data->nvar)+data->ny+k-1] +#define Rid(i) iddata[((i-1)*data->nvar)+data->nr] +#define Pid(i) iddata[((i-1)*data->nvar)+data->np] +#define Mdotid(i) iddata[((i-1)*data->nvar)+data->nm] + +#define Yav(i) Yav[i-1] +#define YAvg(i) YAvg[i-1] +#define YVmhalf(i) YVmhalf[i-1] +#define YVphalf(i) YVphalf[i-1] +#define X(i) X[i-1] +#define Xp(i) Xp[i-1] +#define Xgradhalf(i) Xgradhalf[i-1] +#define XLeft(i) XLeft[i-1] +#define XRight(i) XRight[i-1] +#define gradX(i) gradX[i-1] +#define wdot(i) wdot[i-1] +#define enthalpy(i) enthalpy[i-1] +#define energy(i) energy[i-1] +#define Cp(i) Cp[i-1] + +#define atolT(i) atolvdata[((i-1)*data->nvar)+data->nt] +#define atolY(i,k) atolvdata[((i-1)*data->nvar)+data->ny+k-1] +#define atolR(i) atolvdata[((i-1)*data->nvar)+data->nr] +#define atolP(i) atolvdata[((i-1)*data->nvar)+data->np] +#define atolMdot(i) atolvdata[((i-1)*data->nvar)+data->nm] + + +#define constraintsY(i,k) constraintsdata[((i-1)*data->nvar)+data->ny+k-1] +#define constraintsR(i) constraintsdata[((i-1)*data->nvar)+data->nr] + +#endif diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..e39e3eb --- /dev/null +++ b/main.cpp @@ -0,0 +1,383 @@ +/* + _____ ___ ____ ____ +|_ _/ _ \| _ \ / ___| + | || | | | |_) | | + | || |_| | _ <| |___ + |_| \___/|_| \_\\____| + +*/ + +#include "UserData.h" +#include "solution.h" +#include "residue.h" +#include "macros.h" +#include "timing.h" + +#include +#include +#include +#include +//#include + +static int check_flag(void *flagvalue, + const char *funcname, + int opt); + +void freeAtLast(void* mem, N_Vector *y, + N_Vector *ydot, + N_Vector *res, + N_Vector *id, + N_Vector *atolv, + N_Vector *constraints,UserData data); + +int main(){ + + // Read input file specifying the details of the case and store them + FILE *input;input=fopen("input.dat","r"); + UserData data;data=NULL;data=allocateUserData(input); + fclose(input); + data->clockStart=get_wall_time(); + + if(data==NULL){ + printf("check input file!\n"); + freeUserData(data); + return(-1); + } + + // Allocate solution variables + long int ier,mu,ml,count,netf,ncfn,njevals,nrevals; + realtype tNow,*atolvdata,*constraintsdata,finalTime,dtMax,tolsfac; + + + N_Vector y,ydot,id,res,atolv,constraints; + y=ydot=id=res=atolv=constraints=NULL; + ier=allocateSolution(data->neq,data->nThreads,&y,&ydot,&id,&res,&atolv,&constraints); + ier=setAlgebraicVariables(&id,data); + ier=setInitialCondition(&y,&ydot,data); + if(ier==-1)return(-1); + tNow=data->tNow; + finalTime=data->finalTime; + //TODO: dtMax should be a user input + dtMax = 1e-4; + + double* ydata; + double* ydotdata; + ydata = N_VGetArrayPointer_OpenMP(y); + ydotdata = N_VGetArrayPointer_OpenMP(ydot); + ////////// DEBUG /////////////////// + //double* resdata; + //double* iddata; + //resdata = N_VGetArrayPointer_OpenMP(res); + //iddata = N_VGetArrayPointer_OpenMP(id); + /////////////////////////////////// + + void *mem;mem=NULL;mem = IDACreate(); + ier = IDASetUserData(mem, data); + ier = IDASetId(mem, id); + ier = IDAInit(mem, residue, tNow, y, ydot); + + // Atol array + atolvdata = N_VGetArrayPointer_OpenMP(atolv); + for (size_t i = 1; i <=data->npts; i++) { + atolT(i) = data->temperatureTolerance; + for (size_t k = 1; k <=data->nsp; k++) { + if(k!=data->k_bath){ + atolY(i,k) = data->massFractionTolerance; + } + else{ + atolY(i,k) = data->bathGasTolerance; + } + + } + atolR(i) = data->radiusTolerance; + atolP(i) = data->pressureTolerance; + atolMdot(i) = data->MdotTolerance; + } + ier = IDASVtolerances(mem, data->relativeTolerance, atolv); + + mu = 2*data->nvar; ml = mu; + SUNMatrix A; A=NULL; + A=SUNBandMatrix(data->neq,mu,ml,mu+ml); + SUNLinearSolver LS; LS=NULL; + //LS=SUNBandLinearSolver(y,A); + LS=SUNLapackBand(y,A); + ier=IDADlsSetLinearSolver(mem,LS,A); + //ier = IDABand(mem, data->neq, mu, ml); + + constraintsdata = N_VGetArrayPointer_OpenMP(constraints); + if(data->setConstraints){ + for (size_t i = 1; i <=data->npts; i++) { + for (size_t k = 1; k <=data->nsp; k++) { + constraintsY(i,k) = ONE; + } + } + ier=IDASetConstraints(mem, constraints); + } + + if(!data->quasiSteady){ + constraintsR(1) = ONE; + ier=IDASetConstraints(mem, constraints); + } + + ier = IDASetSuppressAlg(mem, data->suppressAlg); + if(check_flag(&ier, "IDASetSuppressAlg", 1)) return(1); + + //ier= IDASetMaxNumStepsIC(mem, 1); + //ier= IDASetMaxNumJacsIC(mem,8); + //ier= IDASetMaxNumItersIC(mem,100); + //ier= IDASetMaxBacksIC(mem,2000); + //ier = IDASetLineSearchOffIC(mem,SUNTRUE); + + //////// DEBUG /////////// + //if(data->dryRun){ + // ier = residue(tNow,y, ydot, res, data); + // for(size_t k=0; k < data->nvar*data->npts; k++){ + // printf("%i: %15.6e\n",k,resdata[k]); + // } + // for(size_t k=0; k < data->nvar*data->npts; k++){ + // if(iddata[k] == 1){ + // ydotdata[k] = -resdata[k]; + // } + // } + // ier = residue(tNow,y, ydot, res, data); + // for(size_t k=0; k < data->nvar*data->npts; k++){ + // printf("%i: %15.6e\n",k,resdata[k]); + // } + //} + //for(size_t k=0; k < data->neq; k++){ + // if(iddata[k] == 1){ + // ydotdata[k] = -resdata[k]; + // } + //} + ////////////////////////// + + if(!data->dryRun){ + printf("Calculating Initial Conditions:\n"); + printf("Cross your fingers...\n"); + ier = IDACalcIC(mem, IDA_YA_YDP_INIT, 1e-05*finalTime); + //If at first IDACalcIC doesn't succeed, try, try, try again: + for (int i = 0; i < 10; i++) { + ier = IDACalcIC(mem, IDA_YA_YDP_INIT, (1e-01+pow(10,i)*finalTime)); + if(ier==0){ + break; + } + } + + //...or else cry again :( + if(check_flag(&ier, "IDACalcIC", 1)){ + freeAtLast(mem,&y,&ydot,&res,&id,&atolv,&constraints,data); + return(-1); + }else{ + printf("Initial (Consistent) Conditions Calculated!\n"); + } + ier = IDASetInitStep(mem,1e-12); + } + + printSpaceTimeHeader(data); + printGlobalHeader(data); + printSpaceTimeOutput(tNow, &y, data->output, data); + printSpaceTimeOutput(tNow, &y, data->gridOutput, data); + + if(!data->dryRun){ + count=0; + double dt=1e-08; + double t1=0.0e0; + double xOld=0.0e0; + double x=0.0e0; + double dx=0.0e0; + double dxMin=1.0e0; + double dxRatio=dx/dxMin; + int move=0; + int kcur=0; + if(data->adaptiveGrid){ + dxMin=data->grid->leastMove; + //xOld=maxCurvPosition(ydata, data->nt, data->nvar, + // data->grid->x, data->npts); + xOld=isothermPosition(ydata, data->isotherm, data->nt, + data->nvar, data->grid->x, data->npts); + } + while (tNow<=finalTime && R(1)>100e-9) { + + t1=tNow; + + if(data->quasiSteady){ + ier = IDASolve(mem, finalTime, &tNow, y, ydot, IDA_ONE_STEP); + }else{ + /*This prevents the solver from taking a step so large that + *the droplet radius becomes negative. + *TODO:Try adding the constraint that the droplet radius must + * be a positive number*/ + ier = IDASolve(mem, tNow+dtMax, &tNow, y, ydot, IDA_ONE_STEP); + //ier = IDASolve(mem, tNow+dtMax, &tNow, y, ydot, IDA_NORMAL); + } + + if(check_flag(&ier, "IDASolve", 1)){ + freeAtLast(mem,&y,&ydot,&res,&id,&atolv,&constraints,data); + return(-1); + } + dt=tNow-t1; + ier = IDAGetCurrentOrder(mem, &kcur); + + if(data->adaptiveGrid==1 && data->moveGrid==1){ + x=maxCurvPosition(ydata, data->nt, data->nvar, + data->grid->x, data->npts); + + //x=isothermPosition(ydata, data->isotherm, data->nt, + // data->nvar, data->grid->x, data->npts); + + //x=maxGradPosition(ydata, data->nt, data->nvar, + // data->grid->x, data->npts); + dx=x-xOld; + + if(dx*dxMin>0.0e0){ + move=1; + }else{ + move=-1; + } + + //if(fabs(dx)>=dxMin && x+(double)(move)*0.5e0*dxMin<=1.0e0){ + dxRatio=fabs(dx/dxMin); + if(dxRatio>=1.0e0 && dxRatio<=2.0e0){ + printf("Regridding!\n"); + + data->regrid=1; + printSpaceTimeOutput(tNow, &y, data->gridOutput, data); + + ier=reGrid(data->grid, x+(double)(move)*0.5e0*dxMin); + if(ier==-1){ + freeAtLast(mem,&y,&ydot,&res,&id,&atolv,&constraints,data); + return(-1); + } + + updateSolution(ydata, ydotdata, data->nvar, + data->grid->xOld,data->grid->x,data->npts); + storeGrid(data->grid->x,data->grid->xOld,data->npts); + xOld=x; + + printf("Regrid Complete! Restarting Problem at %15.6e s\n",tNow); + ier = IDAReInit(mem, tNow, y, ydot); + if(check_flag(&ier, "IDAReInit", 1)){ + freeAtLast(mem,&y,&ydot,&res,&id,&atolv,&constraints,data); + return(-1); + } + ier = IDASetInitStep(mem,1e-01*dt); + printf("Reinitialized! Calculating Initial Conditions:\n"); + printf("Cross your fingers...\n"); + ier = IDACalcIC(mem, IDA_YA_YDP_INIT, tNow+1e-01*dt); + if(check_flag(&ier, "IDACalcIC", 1)){ + ier = IDACalcIC(mem, IDA_YA_YDP_INIT, tNow+1e+01*dt); + } + //Every once in a while, for reasons + //that befuddle this mathematically + //lesser author, IDACalcIC fails. Here, + //I desperately try to make it converge + //again by sampling increasingly larger + //time-steps: + for (int i = 0; i < 10; i++) { + ier = IDACalcIC(mem, IDA_YA_YDP_INIT, tNow+(1e-01+pow(10,i)*dt)); + if(ier==0){ + break; + } + } + //Failure :( Back to the drawing board: + if(check_flag(&ier, "IDACalcIC", 1)){ + freeAtLast(mem,&y,&ydot,&res,&id,&atolv,&constraints,data); + return(-1); + } + printf("Initial (Consistent) Conditions Calculated!\n"); + printf("------------------------------------------\n\n"); + if(data->writeEveryRegrid){ + printSpaceTimeOutput(tNow, &y, data->output, data); + FILE* fp; + fp=fopen("restart.bin","w"); + writeRestart(tNow, &y, &ydot, fp, data); + fclose(fp); + } + + } + } + + if(count%data->nSaves==0 && !data->writeEveryRegrid){ + printSpaceTimeOutput(tNow, &y, data->output, data); + //if(data->writeRates){ + // printSpaceTimeRates(tNow, ydot, data); + //} + } + + /*Print global variables only if time-step is of high order!*/ + if(data->nTimeSteps==0){ + data->flamePosition[0]=0.0e0; + data->flamePosition[1]=0.0e0; + data->flameTime[0]=tNow; + data->flameTime[1]=tNow; + } + + ier = IDAGetNumErrTestFails(mem, &netf); + ier = IDAGetNumNonlinSolvConvFails(mem, &ncfn); + ier = IDADlsGetNumJacEvals(mem, &njevals); + ier = IDADlsGetNumResEvals(mem, &nrevals); + printf("etf = %ld ," + "nlsf= %ld ," + "J=%ld ," + "R=%ld ," + "o=%d ,",netf, ncfn, njevals, nrevals, kcur); + printf("Time=%15.6e s,",tNow); + printf("frac: %15.6e\n",dxRatio); + count++; + data->nTimeSteps=count; + } + } + + SUNLinSolFree(LS); + SUNMatDestroy(A); + freeAtLast(mem,&y,&ydot,&res,&id,&atolv,&constraints,data); + + return(0); +} + +void freeAtLast(void* mem, + N_Vector *y, + N_Vector *ydot, + N_Vector *res, + N_Vector *id, + N_Vector *atolv, + N_Vector *constraints,UserData data){ + + IDAFree(&mem); + freeSolution(y,ydot,res,id,atolv,constraints); + freeUserData(data); +} + +static int check_flag(void *flagvalue, const char *funcname, int opt) +{ + int *errflag; + + /* Check if SUNDIALS function returned NULL pointer - no memory allocated */ + if (opt == 0 && flagvalue == NULL) { + fprintf(stderr, + "\nSUNDIALS_ERROR: %s() failed - returned NULL pointer\n\n", + funcname); + return(1); + } + + /* Check if flag < 0 */ + else if (opt == 1) { + errflag = (int *) flagvalue; + if (*errflag < 0) { + fprintf(stderr, + "\nSUNDIALS_ERROR: %s() failed with flag = %d\n\n", + funcname, *errflag); + return(1); + } + } + + /* Check if function returned NULL pointer - no memory allocated */ + else if (opt == 2 && flagvalue == NULL) { + fprintf(stderr, + "\nMEMORY_ERROR: %s() failed - returned NULL pointer\n\n", + funcname); + return(1); + } + + return(0); +} diff --git a/parse.cpp b/parse.cpp new file mode 100644 index 0000000..f872ca5 --- /dev/null +++ b/parse.cpp @@ -0,0 +1,20 @@ +#include "parse.h" +void getFromString (const char* buf, int* n){ + *n=atoi(buf); + printf("%d\n",*n); +} + +void getFromString (const char* buf, size_t* n){ + *n=(size_t)(atoi(buf)); + printf("%lu\n",*n); +} + +void getFromString (const char* buf, double* n){ + *n=(double)(atof(buf)); + printf("%15.6e\n",*n); +} + +void getFromString (const char* buf, char* n){ + sscanf(buf,"%s",n); + printf("%s\n",n); +} diff --git a/parse.h b/parse.h new file mode 100644 index 0000000..45f3a2c --- /dev/null +++ b/parse.h @@ -0,0 +1,31 @@ +#ifndef PRINT_DEF +#define PRINT_DEF +#include //for strings +#include //for printf,scanf +#include //for atoi, atof +#endif + +#ifndef PARSE_DEF +#define PARSE_DEF +#define MAXBUFLEN 200 + +void getFromString (const char* buf, int* n); +void getFromString (const char* buf, size_t* n); +void getFromString (const char* buf, double* n); +void getFromString (const char* buf, char* n); + +int parseString(FILE* input, const char* keyword, const size_t bufLen, char* n); + +template +int parseNumber(FILE* input, const char* keyword, const size_t bufLen, T* n); + +template +int parseArray(FILE* input, const char* keyword, const size_t bufLen, + const size_t arrLen, T y[]); + + +#include "parse.hpp" + +#endif + + diff --git a/parse.hpp b/parse.hpp new file mode 100644 index 0000000..634db96 --- /dev/null +++ b/parse.hpp @@ -0,0 +1,76 @@ +template +int parseNumber(FILE* input, const char* keyword, const size_t bufLen, T* n){ + + char buf[bufLen]; + char buf1[bufLen]; + char comment[1]; + char *ret; + + while (fgets(buf,bufLen, input)!=NULL){ + comment[0]=buf[0]; + if(strncmp(comment,"#",1)==0){ + //printf("Comment!:%s\n",buf); + } + else{ + ret=strtok(buf,"="); + if(strcmp(ret,keyword)==0){ + /*offset buf by keyword size + 1 for the "="*/ + strncpy (buf1, buf+strlen(keyword)+1, bufLen); + printf("%30s: ",keyword); + getFromString(buf1,n); + rewind(input); + return(0); + } + } + } + rewind(input); + return(-1); +} + +template +int parseArray(FILE* input, const char* keyword, const size_t bufLen, + const size_t arrLen, T y[]){ + + char buf[bufLen]; + char buf1[bufLen]; + char comment[1]; + char *ret; + + while (fgets(buf,bufLen, input)!=NULL){ + comment[0]=buf[0]; + if(strncmp(comment,"#",1)==0){ + //printf("Comment!:%s\n",buf); + } + else{ + ret=strtok(buf,"="); + + if(strcmp(ret,keyword)==0){ + /*offset buf by keyword size + 1 for the "="*/ + strncpy (buf1, buf+strlen(keyword)+1, bufLen); + printf("%30s:\n",keyword); + ret=strtok(buf1,","); + size_t j=0; + while(ret!=NULL){ + if(j +#include +#endif +#include "residue.h" +#include "macros.h" +#include +#include +#include "timing.hpp" + +double maxGradPosition(const double* y, const size_t nt, + const size_t nvar, const double* x, size_t nPts){ + double maxGradT=0.0e0; + double gradT=0.0e0; + double pos=0.0e0; + size_t j,jm; + for (size_t i = 1; i =maxGradT) { + maxGradT=gradT; + pos=x[i]; + } + } + return(pos); +} + +int maxGradIndex(const double* y, const size_t nt, + const size_t nvar, const double* x, size_t nPts){ + double maxGradT=0.0e0; + double gradT=0.0e0; + int pos=0.0e0; + size_t j,jm; + for (size_t i = 1; i =maxGradT) { + maxGradT=gradT; + pos=i; + } + } + return(pos); +} + +double maxCurvPosition(const double* y, const size_t nt, + const size_t nvar, const double* x, size_t nPts){ + double maxCurvT=0.0e0; + double gradTp=0.0e0; + double gradTm=0.0e0; + double curvT=0.0e0; + double dx=0.0e0; + double pos=0.0e0; + size_t j,jm,jp; + for (size_t i = 1; i =maxCurvT) { + maxCurvT=curvT; + pos=x[i]; + } + } + return(pos); +} + +int maxCurvIndex(const double* y, const size_t nt, + const size_t nvar, const double* x, size_t nPts){ + double maxCurvT=0.0e0; + double gradTp=0.0e0; + double gradTm=0.0e0; + double curvT=0.0e0; + double dx=0.0e0; + int pos=0; + size_t j,jm,jp; + for (size_t i = 1; i =maxCurvT) { + maxCurvT=curvT; + pos=i; + } + } + return(pos); +} + +double isothermPosition(const double* y, const double T, const size_t nt, + const size_t nvar, const double* x, const size_t nPts){ + double pos=x[nPts-1]; + size_t j; + for (size_t i = 1; i gas->massFraction(0); + for(size_t k=1;knsp;k++){ + max1=data->gas->massFraction(k); + if(max1>=max){ + max=max1; + index=k; + } + } + return(index+1); +} + +//Locate Oxidizer: +size_t oxidizerIndex(UserData data){ + size_t index=0; + for(size_t k=1;knsp;k++){ + if(data->gas->speciesName(k-1)=="O2"){ + index=k; + } + } + return(index); +} + +//Locate OH: +size_t OHIndex(UserData data){ + size_t index=0; + for(size_t k=1;knsp;k++){ + if(data->gas->speciesName(k-1)=="OH"){ + index=k; + } + } + return(index); +} + +//Locate HO2: +size_t HO2Index(UserData data){ + size_t index=0; + for(size_t k=1;knsp;k++){ + if(data->gas->speciesName(k-1)=="HO2"){ + index=k; + } + } + return(index); +} + +//Locate species index: +size_t specIndex(UserData data,char *specName){ + size_t index=0; + for(size_t k=1;knsp;k++){ + if(data->gas->speciesName(k-1)==specName){ + index=k; + } + } + return(index); +} + +int setAlgebraicVariables(N_Vector* id, UserData data){ + double *iddata; + N_VConst(ONE, *id); + iddata = N_VGetArrayPointer_OpenMP(*id); + data->k_bath=BathGasIndex(data); + data->k_oxidizer=oxidizerIndex(data); + data->k_OH=OHIndex(data); + data->k_HO2=HO2Index(data); + data->k_drop=specIndex(data,data->dropSpec); + printf("Oxidizer index: %lu\n",data->k_oxidizer); + printf("Bath gas index:%lu\n",data->k_bath); + printf("Droplet species index:%lu\n",data->k_drop); + for (size_t i = 1; i <=data->npts; i++) { + /*Algebraic variables: indicated by ZERO.*/ + Rid(i)=ZERO; + Pid(i)=ZERO; + Yid(i,data->k_bath)=ZERO; + Mdotid(i)=ZERO; + } + Mdotid(1)=ZERO; + Rid(1)=ONE; + Yid(1,data->k_drop)=ZERO; + Tid(data->npts)=ZERO; + if(data->constantPressure){ + Pid(data->npts)=ONE; + }else{ + Pid(data->npts)=ZERO; + Rid(data->npts)=ONE; + } + if(data->dirichletInner){ + Tid(1)=ZERO; + }else{ + Tid(1)=ONE; + } + if(data->dirichletOuter){ + for (size_t k = 1; k <=data->nsp; k++) { + if(k!=data->k_bath){ + Yid(data->npts,k)=ONE; + } + Yid(1,k)=ZERO; + Tid(data->npts)=ONE; + } + }else{ + for (size_t k = 1; k <=data->nsp; k++){ + Yid(1,k)=ZERO; + Yid(data->npts,k)=ZERO; + Tid(data->npts)=ZERO; + } + } + return(0); +} + +inline double calc_area(double x,int* i){ + switch (*i) { + case 0: + return(ONE); + case 1: + return(x); + case 2: + return(x*x); + default: + return(ONE); + } +} + +void readInitialCondition(FILE* input, double* ydata, const size_t nvar, const size_t nr, const size_t nPts, double Rg){ + + FILE* output;output=fopen("test.dat","w"); + + size_t bufLen=10000; + size_t nRows=0; + size_t nColumns=nvar; + + char buf[bufLen]; + char buf1[bufLen]; + char comment[1]; + char *ret; + + while (fgets(buf,bufLen, input)!=NULL){ + comment[0]=buf[0]; + if(strncmp(comment,"#",1)!=0){ + nRows++; + } + } + rewind(input); + + printf("nRows: %ld\n", nRows); + double y[nRows*nColumns]; + + size_t i=0; + while (fgets(buf,bufLen, input)!=NULL){ + comment[0]=buf[0]; + if(strncmp(comment,"#",1)==0){ + } + else{ + ret=strtok(buf,"\t"); + size_t j=0; + y[i*nColumns+j]=(double)(atof(ret)); + j++; + while(ret!=NULL){ + ret=strtok(NULL,"\t"); + if(jnpts; i++) { + data->gas->setState_TPY(T(i), P(i), &Y(i,1)); + rho=data->gas->density(); + //psi(i)=psi(i-1)+rho*(R(i)-R(i-1))*calc_area(HALF*(R(i)+R(i-1)),&m); + mass+=rho*(R(i)-R(i-1))*calc_area(R(i),&data->metric); + } + return(mass); +} + + +int initializePsiGrid(double* ydata, double* psidata, UserData data){ + + double rho,rhom; + /*Create a psi grid that corresponds CONSISTENTLY to the spatial grid + * "R" created above. Note that the Lagrangian variable psi has units + * of kg. */ + psi(1)=ZERO; + for (size_t i = 2; i <=data->npts; i++) { + data->gas->setState_TPY(T(i), P(i), &Y(i,1)); + rho=data->gas->density(); + data->gas->setState_TPY(T(i-1), P(i-1), &Y(i-1,1)); + rhom=data->gas->density(); + //psi(i)=psi(i-1)+rho*(R(i)-R(i-1))*calc_area(HALF*(R(i)+R(i-1)),&data->metric); + //psi(i)=psi(i-1)+rho*(R(i)-R(i-1))*calc_area(R(i),&data->metric); + psi(i)=psi(i-1)+(R(i)-R(i-1))*(rho*calc_area(R(i),&data->metric)+rhom*calc_area(R(i-1),&data->metric))/TWO; + } + + /*The mass of the entire system is the value of psi at the last grid + * point. Normalize psi by this mass so that it varies from zero to + * one. This makes psi dimensionless. So the mass needs to be + * multiplied back in the approporiate places in the governing + * equations so that units match.*/ + data->mass=psi(data->npts); + for (size_t i = 1; i <=data->npts; i++) { + psi(i)=psi(i)/data->mass; + } + return(0); +} + + +int setInitialCondition(N_Vector* y, + N_Vector* ydot, + UserData data){ + + double* ydata; + double* ydotdata; + double* psidata; + double* innerMassFractionsData, Rd, massDrop; + double rhomhalf, lambdamhalf, YVmhalf[data->nsp]; + double f=ZERO; + double g=ZERO; + + double perturb,rho; + double epsilon=ZERO; + int m,ier; + ydata = N_VGetArrayPointer_OpenMP(*y); + ydotdata = N_VGetArrayPointer_OpenMP(*ydot); + innerMassFractionsData = data->innerMassFractions; + Rd = data->Rd; + massDrop = data->massDrop; + + //Mass of droplet + massDrop=1.0/3.0*Rd*Rd*Rd*997.0; //TODO: The density of the droplet should be a user input + + if(data->adaptiveGrid){ + psidata = data->grid->xOld; + } + else{ + psidata = data->uniformGrid; + } + + m=data->metric; + + data->innerTemperature=data->initialTemperature; + for (size_t k = 1; k <=data->nsp; k++) { + innerMassFractionsData[k-1]=data->gas->massFraction(k-1); + } + + //Define Grid: + double dR=(data->domainLength)/((double)(data->npts)-1.0e0); + double dv=(pow(data->domainLength,1+data->metric)-pow(data->firstRadius*data->domainLength,1+data->metric))/((double)(data->npts)-1.0e0); + for (size_t i = 1; i <=data->npts; i++) { + if(data->metric==0){ + R(i)=Rd+(double)((i-1)*dR); + }else{ + if(i==1){ + R(i)=ZERO; + }else if(i==2){ + R(i)=data->firstRadius*data->domainLength; + }else{ + R(i)=pow(pow(R(i-1),1+data->metric)+dv,1.0/((double)(1+data->metric))); + } + } + T(i)=data->initialTemperature; + for (size_t k = 1; k <=data->nsp; k++) { + Y(i,k)=data->gas->massFraction(k-1); //Indexing different in Cantera + } + P(i)=data->initialPressure*Cantera::OneAtm; + } + R(data->npts)=data->domainLength+data->Rd; + + double Tmax; + double Tmin=data->initialTemperature; + double w=data->mixingWidth; + double YN2=ZERO; + double YO2=ZERO; + double YFuel,YOxidizer,sum; + + //if(data->problemType==0){ + // data->gas->equilibrate("HP"); + // data->maxTemperature=data->gas->temperature(); + //} + //else if(data->problemType==1){ + // /*Premixed Combustion: Equilibrium products comprise ignition + // * kernel at t=0. The width of the kernel is "mixingWidth" + // * shifted by "shift" from the center.*/ + // data->gas->equilibrate("HP"); + // Tmax=data->gas->temperature(); + // for (size_t i = 1; i <=data->npts; i++) { + // g=HALF*(tanh((R(i)-data->shift)/w)+ONE); //increasing function of x + // f=ONE-g; //decreasing function of x + // T(i)=(Tmax-Tmin)*f+Tmin; + // for (size_t k = 1; k <=data->nsp; k++) { + // Y(i,k)=(data->gas->massFraction(k-1)-Y(i,k))*f+Y(i,k); + // } + // } + // if(data->dirichletOuter){ + // T(data->npts)=data->wallTemperature; + // } + //} + //else if(data->problemType==2){ + // FILE* input; + // if(input=fopen("initialCondition.dat","r")){ + // readInitialCondition(input, ydata, data->nvar, data->nr, data->npts); + // fclose(input); + // } + // else{ + // printf("file initialCondition.dat not found!\n"); + // return(-1); + // } + //} + + initializePsiGrid(ydata,psidata,data); + + if(data->adaptiveGrid){ + // if(data->problemType!=0){ + // data->grid->position=maxGradPosition(ydata, data->nt, data->nvar, + // data->grid->xOld, data->npts); + // //data->grid->position=maxCurvPosition(ydata, data->nt, data->nvar, + // // data->grid->xOld, data->npts); + // } + // else{ + // } + if(data->problemType!=3){ + data->grid->position=0.0e0; + double x=data->grid->position+data->gridOffset*data->grid->leastMove; + printf("New grid center:%15.6e\n",x); + ier=reGrid(data->grid, x); + if(ier==-1)return(-1); + updateSolution(ydata, ydotdata, data->nvar, + data->grid->xOld,data->grid->x,data->npts); + storeGrid(data->grid->x,data->grid->xOld,data->npts); + } + } + else{ + double Rg = data->Rg, dpsi0; + int NN = data->npts-2; + double psiNew[data->npts]; + + dpsi0 = (pow(Rg,1.0/NN) - 1.0)/(pow(Rg,(NN+1.0)/NN) - 1.0); + psiNew[0] = 0; + for (size_t i = 1; i < data->npts-1; i++) { + psiNew[i]=psiNew[i-1]+dpsi0*pow(Rg,(i-1.0)/NN); + } + psiNew[data->npts-1] = 1.0; + printf("Last point:%15.6e\n",psiNew[data->npts-1]); + updateSolution(ydata, ydotdata, data->nvar, + data->uniformGrid,psiNew,data->npts); + storeGrid(psiNew,data->uniformGrid,data->npts); + + //double psiNew[data->npts]; + //double dpsi=1.0e0/((double)(data->npts)-1.0e0); + //for (size_t i = 0; i < data->npts; i++) { + // psiNew[i]=(double)(i)*dpsi; + //} + //printf("Last point:%15.6e\n",psiNew[data->npts-1]); + //updateSolution(ydata, ydotdata, data->nvar, + // data->uniformGrid,psiNew,data->npts); + //storeGrid(psiNew,data->uniformGrid,data->npts); + } + + if(data->problemType==0){ + data->gas->equilibrate("HP"); + data->maxTemperature=data->gas->temperature(); + } + else if(data->problemType==1){ + /*Premixed Combustion: Equilibrium products comprise ignition + * kernel at t=0. The width of the kernel is "mixingWidth" + * shifted by "shift" from the center.*/ + data->gas->equilibrate("HP"); + Tmax=data->gas->temperature(); + for (size_t i = 1; i <=data->npts; i++) { + g=HALF*(tanh((R(i)-data->shift)/w)+ONE); //increasing function of x + f=ONE-g; //decreasing function of x + T(i)=(Tmax-Tmin)*f+Tmin; + for (size_t k = 1; k <=data->nsp; k++) { + Y(i,k)=(data->gas->massFraction(k-1)-Y(i,k))*f+Y(i,k); + } + } + if(data->dirichletOuter){ + T(data->npts)=data->wallTemperature; + } + } + else if(data->problemType==2){ + FILE* input; + if(input=fopen("initialCondition.dat","r")){ + readInitialCondition(input, ydata, data->nvar, data->nr, data->npts, data->Rg); + fclose(input); + } + else{ + printf("file initialCondition.dat not found!\n"); + return(-1); + } + initializePsiGrid(ydata,psidata,data); + } + else if(data->problemType==3){ + FILE* input; + if(input=fopen("restart.bin","r")){ + readRestart(y, ydot, input, data); + fclose(input); + printf("Restart solution loaded!\n"); + printf("Problem starting at t=%15.6e\n",data->tNow); + return(0); + } + else{ + printf("file restart.bin not found!\n"); + return(-1); + } + } + + if(data->reflectProblem){ + double temp; + int j=1; + while (data->npts+1-2*j>=0) { + temp=T(j); + T(j)=T(data->npts+1-j); + T(data->npts+1-j)=temp; + for (size_t k = 1; k <=data->nsp; k++) { + temp=Y(j,k); + Y(j,k)=Y(data->npts+1-j,k); + Y(data->npts+1-j,k)=temp; + } + j=j+1; + } + } + + /*Floor small values to zero*/ + for (size_t i = 1; i <=data->npts; i++) { + for (size_t k = 1; k <=data->nsp; k++) { + if(fabs(Y(i,k))<=data->massFractionTolerance){ + Y(i,k)=0.0e0; + } + } + } + + //Set grid to location of maximum curvature: + if(data->adaptiveGrid){ + data->grid->position=maxCurvPosition(ydata, data->nt, data->nvar, + data->grid->x, data->npts); + ier=reGrid(data->grid, data->grid->position); + updateSolution(ydata, ydotdata, data->nvar, + data->grid->xOld,data->grid->x,data->npts); + storeGrid(data->grid->x,data->grid->xOld,data->npts); + } + + /*Ensure consistent boundary conditions*/ + //T(1)=T(2); + //for (size_t k = 1; k <=data->nsp; k++) { + // Y(1,k)=Y(2,k); + // Y(data->npts,k)=Y(data->npts-1,k); + //} + + return(0); +} + +inline double Qdot(double* t, + double* x, + double* ignTime, + double* kernelSize, + double* maxQdot){ + double qdot; + if(*x<=*kernelSize){ + if((*t)<=(*ignTime)){ + qdot=(*maxQdot); + } + else{ + qdot=0.0e0; + } + }else{ + qdot=0.0e0; + } + return(qdot); +} + +inline void setGas(UserData data, + double *ydata, + size_t gridPoint){ + data->gas->setTemperature(T(gridPoint)); + data->gas->setMassFractions_NoNorm(&Y(gridPoint,1)); + data->gas->setPressure(P(gridPoint)); +} + +void getTransport(UserData data, + double *ydata, + size_t gridPoint, + double *rho, + double *lambda, + double YV[]){ + + double YAvg[data->nsp], + XLeft[data->nsp], + XRight[data->nsp], + gradX[data->nsp]; + + setGas(data,ydata,gridPoint); + data->gas->getMoleFractions(XLeft); + setGas(data,ydata,gridPoint+1); + data->gas->getMoleFractions(XRight); + + for (size_t k = 1; k <=data->nsp; k++) { + YAvg(k)=HALF*(Y(gridPoint,k)+ + Y(gridPoint+1,k)); + gradX(k)=(XRight(k)-XLeft(k))/ + (R(gridPoint+1)-R(gridPoint)); + } + double TAvg = HALF*(T(gridPoint)+T(gridPoint+1)); + double gradT=(T(gridPoint+1)-T(gridPoint))/ + (R(gridPoint+1)-R(gridPoint)); + + + data->gas->setTemperature(TAvg); + data->gas->setMassFractions_NoNorm(YAvg); + data->gas->setPressure(P(gridPoint)); + + *rho=data->gas->density(); + *lambda=data->trmix->thermalConductivity(); + data->trmix->getSpeciesFluxes(1,&gradT,data->nsp, + gradX,data->nsp,YV); + //setGas(data,ydata,gridPoint); +} + +int residue(double t, N_Vector y, N_Vector ydot, N_Vector res, void *user_data){ + + /*Declare and fetch nvectors and user data:*/ + + double *ydata, *ydotdata, *resdata, *psidata, *innerMassFractionsData; + + UserData data; + data = (UserData)user_data; + size_t npts=data->npts; + size_t nsp=data->nsp; + size_t k_bath = data->k_bath; + size_t k_drop = data->k_drop; + + ydata = N_VGetArrayPointer_OpenMP(y); + ydotdata= N_VGetArrayPointer_OpenMP(ydot); + resdata = N_VGetArrayPointer_OpenMP(res); + if(data->adaptiveGrid==1){ + psidata = data->grid->x; + }else{ + psidata = data->uniformGrid; + } + + innerMassFractionsData = data->innerMassFractions; + + /* Grid stencil:*/ + + /*-------|---------*---------|---------*---------|-------*/ + /*-------|---------*---------|---------*---------|-------*/ + /*-------|---------*---------|---------*---------|-------*/ + /*-------m-------mhalf-------j-------phalf-------p-------*/ + /*-------|---------*---------|---------*---------|-------*/ + /*-------|---------*---------|---------*---------|-------*/ + /*-------|<=======dxm=======>|<=======dxp=======>|-------*/ + /*-------|---------*<======dxav=======>*---------|-------*/ + /*-------|<================dxpm=================>|-------*/ + + /* Various variables defined for book-keeping and storing previously + * calculated values: + * rho : densities at points m, mhalf, j, p, and phalf. + * area : the matric at points m, mhalf, j, p, and phalf. + * m : exponent that determines geometry; + * lambda : thermal conductivities at mhalf and phalf. + * mdot : mass flow rate at m, j, and p. + * X : mole fractions at j and p. + * YV : diffusion fluxes at mhalf and phalf. + * Tgrad : temperature gradient at mhalf and phalf. + * Tav : average temperature between two points. + * Pav : average pressure between two points. + * Yav : average mass fractions between two points. + * Xgradhalf : mole fraction gradient at j. + * Cpb : mass based bulk specific heat. + * tranTerm : transient terms. + * advTerm : advection terms. + * diffTerm : diffusion terms. + * srcTerm : source terms. + */ + + double rhomhalf, rhom, lambdamhalf, YVmhalf[nsp], + rho, + rhophalf, lambdaphalf, YVphalf[nsp], + Cpb, Cvb, Cp[nsp], Cpl, dHvl, rhol, wdot[nsp], enthalpy[nsp], energy[nsp], + tranTerm, diffTerm, srcTerm, advTerm, + area,areamhalf,areaphalf,aream,areamhalfsq,areaphalfsq; + + /*Aliases for difference coefficients:*/ + double cendfm, cendfc, cendfp; + /*Aliases for various grid spacings:*/ + double dpsip, dpsiav, dpsipm, dpsim, dpsimm; + dpsip=dpsiav=dpsipm=dpsim=dpsimm=ONE; + //double mass, mdotIn; + double mass, massDrop; + double sum, sum1, sum2, sum3; + + size_t j,k; + int m; + m=data->metric; //Unitless + mass=data->mass; //Units: kg + //massDrop=data->massDrop; //Units: kg + //mdotIn=data->mdot*calc_area(R(npts),&m); //Units: kg/s + +// /*evaluate properties at j=1*************************/ + setGas(data,ydata,1); + rhom=data->gas->density(); + Cpb=data->gas->cp_mass(); //J/kg/K + Cvb=data->gas->cv_mass(); //J/kg/K + //TODO: Create user input model for these. They should not be constant + //Cpl=4.182e03; //J/kg/K for liquid water + Cpl= 5.67508633e-07*pow(T(1),4) - 7.78060597e-04*pow(T(1),3) + 4.08310544e-01*pow(T(1),2) //J/kg/K for liquid water + - 9.62429538e+01*T(1) + 1.27131046e+04; + + //dHvl=2.260e6; //J/kg heat of vaporization of water + double Tr = T(1)/647.1; //Reduced Temperature: Droplet Temperature divided by critical temperature of water + dHvl= 5.2053e07*pow(1-Tr,0.3199 - 0.212*Tr + 0.25795*Tr*Tr)/18.01; //J/kg latent heat of vaporization of water + rhol = 997.0; + massDrop=1.0/3.0*R(1)*R(1)*R(1)*997.0; //TODO: The density of the droplet should be a user input + aream= calc_area(R(1),&m); + + /*******************************************************************/ + /*Calculate values at j=2's m and mhalf*****************************/ + + getTransport(data, ydata, 1, &rhomhalf,&lambdamhalf,YVmhalf); + areamhalf= calc_area(HALF*(R(1)+R(2)),&m); + aream = calc_area(R(1),&m); + areamhalfsq= areamhalf*areamhalf; + + /*******************************************************************/ + + /*Fill up res with left side (center) boundary conditions:**********/ + /*We impose zero fluxes at the center:*/ + + + /*Mass:*/ + if(data->quasiSteady){ + Rres(1)=Rdot(1); // Should remain satisfied for quasi-steady droplet + }else{ + Rres(1)=Rdot(1) + Mdot(1)/rhol/aream; + } + + /*Species:*/ + sum=ZERO; + + //TODO: Antoine Parameters should be part of user input, not hardcoded + Yres(1,k_drop)=Y(1,k_drop) - 1.0e5*pow(10,4.6543-(1435.264/(T(1)-64.848)))*data->gas->molecularWeight(k_drop-1) + / P(1) / data->gas->meanMolecularWeight(); + //Yres(1,k_drop)=Y(1,k_drop) - 1.0e3*pow(2.71828,16.7-(4060.0/(T(1)-37.0)))*data->gas->molecularWeight(k_drop-1) + // / P(1) / data->gas->meanMolecularWeight(); + //Yres(1,k_drop)=Y(1,k_drop)-2.566785e-02; + sum=sum+Y(1,k_drop); + + Mdotres(1)=Mdot(1) - (YVmhalf(k_drop)*areamhalf) / (1-Y(1,k_drop)); //Evaporating mass + + for (k = 1; k <=nsp; k++) { + if(k!=k_bath && k!=k_drop){ + //TODO: May need to include chemical source term + Yres(1,k)=Y(1,k)*Mdot(1) + YVmhalf(k)*areamhalf; + //Yres(1,k)=YVmhalf(k)*areamhalf; + sum=sum+Y(1,k); + //if(fabs(Mdot(1))>1e-14){ + ///Yres(1,k)=innerMassFractionsData[k-1]- + /// Y(1,k)- + /// (YVmhalf(k)*areamhalf)/Mdot(1); + //Yres(1,k)=Y(1,k)*Mdot(1) + YVmhalf(k)*areamhalf; + //} + //else{ + // //Yres(1,k)=Y(1,k)-innerMassFractionsData[k-1]; + // Yres(1,k)=Y(2,k)-Y(1,k); + // /*The below flux boundary condition makes the + // * problem more prone to diverge. How does one + // * fix this?*/ + // //Yres(1,k)=YVmhalf(k); + //} + } + } + Yres(1,k_bath)=ONE-sum-Y(1,k_bath); + + /*Energy:*/ + if(data->dirichletInner){ + //Tres(1)=Tdot(1); + //Tres(1)=T(1)-data->innerTemperature; + Tres(1)=lambdamhalf*rhomhalf*areamhalfsq*(T(2)-T(1))/((psi(2)-psi(1))*mass)/dHvl - YVmhalf(k_drop)*areamhalf/(1-Y(1,k_drop)); + }else{ + //Tres(1)=Tdot(1) - + // (rhomhalf*areamhalfsq*lambdamhalf*(T(2)-T(1))/((psi(2)-psi(1))*mass) - Mdot(1)*dHvl) + // / (massDrop * Cpl); + Tres(1)=Tdot(1) - + (areamhalf*lambdamhalf*(T(2)-T(1))/(R(2)-R(1)) - Mdot(1)*dHvl) + / (massDrop * Cpl); + + + + //Tres(1)=T(2)-T(1); + //Tres(1)=Tdot(1) - (Pdot(1)/(rhom*Cpb)) + // +(double)(data->metric+1)*(rhomhalf*lambdamhalf*areamhalfsq*(T(2)-T(1))/psi(2)-psi(1)); + } + + /*Pressure:*/ + Pres(1)=P(2)-P(1); + /*Fill up res with governing equations at inner points:*************/ + for (j = 2; j < npts; j++) { + + /*evaluate various mesh differences*/// + dpsip = (psi(j+1) - psi(j) )*mass; + dpsim = (psi(j) - psi(j-1))*mass; + dpsiav = HALF*(psi(j+1) - psi(j-1))*mass; + dpsipm = (psi(j+1) - psi(j-1))*mass; + /***********************************/// + + /*evaluate various central difference coefficients*/ + cendfm = - dpsip / (dpsim*dpsipm); + cendfc = (dpsip-dpsim) / (dpsip*dpsim); + cendfp = dpsim / (dpsip*dpsipm); + /**************************************************/ + + + /*evaluate properties at j*************************/ + setGas(data,ydata,j); + rho=data->gas->density(); //kg/m^3 + Cpb=data->gas->cp_mass(); //J/kg/K + Cvb=data->gas->cv_mass(); //J/kg/K + data->gas->getNetProductionRates(wdot); //kmol/m^3 + data->gas->getEnthalpy_RT(enthalpy); //unitless + data->gas->getCp_R(Cp); //unitless + area = calc_area(R(j),&m); //m^2 + + /*evaluate properties at p*************************/ + getTransport(data, ydata, j, &rhophalf,&lambdaphalf,YVphalf); + areaphalf= calc_area(HALF*(R(j)+R(j+1)),&m); + areaphalfsq= areaphalf*areaphalf; + /**************************************************/// + + /*Evaporating Mass*/ + Mdotres(j)=Mdot(j) - Mdot(j-1); + + /*Mass:*/ + /* ∂r/∂ψ = 1/ρA */ + Rres(j)=((R(j)-R(j-1))/dpsim)-(TWO/(rhom*aream+rho*area)); + + /*Energy:*/ + /* ∂T/∂t = - ṁ(∂T/∂ψ) + * + (∂/∂ψ)(λρA²∂T/∂ψ) + * - (A/cₚ) ∑ YᵢVᵢcₚᵢ(∂T/∂ψ) + * - (1/ρcₚ)∑ ώᵢhᵢ + * + (1/ρcₚ)(∂P/∂t) */ + /*Notes: + * λ has units J/m/s/K. + * YᵢVᵢ has units kg/m^2/s. + * hᵢ has units J/kmol, so we must multiply the enthalpy + * defined above (getEnthalpy_RT) by T (K) and the gas constant + * (J/kmol/K) to get the right units. + * cₚᵢ has units J/kg/K, so we must multiply the specific heat + * defined above (getCp_R) by the gas constant (J/kmol/K) and + * divide by the molecular weight (kg/kmol) to get the right + * units. + * */ + + //enthalpy formulation: + //tranTerm = Tdot(j) - (Pdot(j)/(rho*Cpb)); + tranTerm = Tdot(j); + sum=ZERO; + sum1=ZERO; + for (k = 1; k <=nsp; k++) { + sum=sum+wdot(k)*enthalpy(k); + sum1=sum1+(Cp(k)/data->gas->molecularWeight(k-1))*rho + *HALF*(YVmhalf(k)+YVphalf(k)); + } + sum=sum*Cantera::GasConstant*T(j); + sum1=sum1*Cantera::GasConstant; + diffTerm =(( (rhophalf*areaphalfsq*lambdaphalf*(T(j+1)-T(j))/dpsip) + -(rhomhalf*areamhalfsq*lambdamhalf*(T(j)-T(j-1))/dpsim) ) + /(dpsiav*Cpb) ) + -(sum1*area*(cendfp*T(j+1) + +cendfc*T(j) + +cendfm*T(j-1))/Cpb); + srcTerm = (sum-Qdot(&t,&R(j),&data->ignTime,&data->kernelSize,&data->maxQDot))/(rho*Cpb); // Qdot is forced heating to cause ignition, sum is the conversion of chemical enthalpy to sensible enthalpy + advTerm = (Mdot(j)*(T(j)-T(j-1))/dpsim); + Tres(j)= tranTerm - (Pdot(j)/(rho*Cpb)) + +advTerm + -diffTerm + +srcTerm; + + // //energy formulation: + // tranTerm = Tdot(j); + // sum=ZERO; + // sum1=ZERO; + // sum2=ZERO; + // sum3=ZERO; + // for (k = 1; k <=nsp; k++) { + // energy(k)=enthalpy(k)-ONE; + // sum=sum+wdot(k)*energy(k); + // sum1=sum1+(Cp(k)/data->gas->molecularWeight(k-1))*rho + // *HALF*(YVmhalf(k)+YVphalf(k)); + // sum2=sum2+(YVmhalf(k)/data->gas->molecularWeight(k-1)); + // sum3=sum3+(YVphalf(k)/data->gas->molecularWeight(k-1)); + // } + // sum=sum*Cantera::GasConstant*T(j); + // sum1=sum1*Cantera::GasConstant; + // diffTerm =(( (rhophalf*areaphalfsq*lambdaphalf*(T(j+1)-T(j))/dpsip) + // -(rhomhalf*areamhalfsq*lambdamhalf*(T(j)-T(j-1))/dpsim) ) + // /(dpsiav*Cvb) ) + // -(sum1*area*(cendfp*T(j+1) + // +cendfc*T(j) + // +cendfm*T(j-1))/Cvb); + // srcTerm = (sum-Qdot(&t,&R(j),&data->ignTime,&data->kernelSize,&data->maxQDot))/(rho*Cvb); + // advTerm = (mdotIn*(T(j)-T(j-1))/dpsim); + // advTerm = advTerm + (Cantera::GasConstant*T(j)*area/Cvb)*((sum3-sum2)/dpsiav); + // Tres(j)= tranTerm + // +advTerm + // -diffTerm + // +srcTerm; + + /*Species:*/ + /* ∂Yᵢ/∂t = - ṁ(∂Yᵢ/∂ψ) + * - (∂/∂ψ)(AYᵢVᵢ) + * + (ώᵢWᵢ/ρ) */ + + sum=ZERO; + for (k = 1; k <=nsp; k++) { + if(k!=k_bath){ + tranTerm = Ydot(j,k); + diffTerm = (YVphalf(k)*areaphalf + -YVmhalf(k)*areamhalf)/dpsiav; + srcTerm = wdot(k) + *(data->gas->molecularWeight(k-1))/rho; + advTerm = (Mdot(j)*(Y(j,k)-Y(j-1,k))/dpsim); + + Yres(j,k)= tranTerm + +advTerm + +diffTerm + -srcTerm; + + sum=sum+Y(j,k); + } + } + Yres(j,k_bath)=ONE-sum-Y(j,k_bath); + + /*Pressure:*/ + Pres(j) = P(j+1)-P(j); + + /*Assign values evaluated at p and phalf to m + * and mhalf to save some cpu cost:****************/ + areamhalf=areaphalf; + areamhalfsq=areaphalfsq; + aream=area; + rhom=rho; + rhomhalf=rhophalf; + lambdamhalf=lambdaphalf; + for (k = 1; k <=nsp; k++) { + YVmhalf(k)=YVphalf(k); + } + /**************************************************/ + + } + /*******************************************************************/// + + + /*Fill up res with right side (wall) boundary conditions:***********/ + /*We impose zero fluxes at the wall:*/ + setGas(data,ydata,npts); + rho=data->gas->density(); + area = calc_area(R(npts),&m); + + /*Mass:*/ + dpsim=(psi(npts)-psi(npts-1))*mass; + Rres(npts)=((R(npts)-R(npts-1))/dpsim)-(TWO/(rhom*aream+rho*area)); + + /*Energy:*/ + if(data->dirichletOuter){ + //Tres(npts)=T(npts)-data->wallTemperature; + Tres(npts)=Tdot(npts); + } + else{ + Tres(npts)=T(npts)-T(npts-1); + } + + /*Species:*/ + sum=ZERO; + if(data->dirichletOuter){ + for (k = 1; k <=nsp; k++) { + if(k!=k_bath){ + Yres(npts,k)=Ydot(npts,k); + sum=sum+Y(npts,k); + } + } + } + else{ + for (k = 1; k <=nsp; k++) { + if(k!=k_bath){ + Yres(npts,k)=Y(npts,k)-Y(npts-1,k); + //Yres(npts,k)=YVmhalf(k); + sum=sum+Y(npts,k); + } + } + } + Yres(npts,k_bath)=ONE-sum-Y(npts,k_bath); + + + /*Pressure:*/ + if(data->constantPressure){ + Pres(npts)=Pdot(npts)-data->dPdt; + } + else{ + Pres(npts)=R(npts)-data->domainLength; + //Pres(npts)=Rdot(npts); + } + + /*Evaporating Mass*/ + Mdotres(npts)=Mdot(npts) - Mdot(npts-1); + + //for (j = 1; j <=npts; j++) { + // //for (k = 1; k <=nsp; k++) { + // // Yres(j,k)=Ydot(j,k); + // //} + // //Tres(j)=Tdot(j); + //} + + return(0); + +} + +void printSpaceTimeHeader(UserData data) +{ + fprintf((data->output), "%15s\t","#1"); + for (size_t k = 1; k <=data->nvar+1; k++) { + fprintf((data->output), "%15lu\t",k+1); + } + fprintf((data->output), "%15lu\n",data->nvar+3); + + fprintf((data->output), "%15s\t%15s\t%15s\t","#psi","time(s)","dpsi"); + fprintf((data->output), "%15s\t%15s\t","radius(m)","Temp(K)"); + for (size_t k = 1; k <=data->nsp; k++) { + fprintf((data->output), "%15s\t",data->gas->speciesName(k-1).c_str()); + } + fprintf((data->output), "%15s\t","Pressure(Pa)"); + fprintf((data->output), "%15s\n","Mdot (kg/s)"); +} + +void printSpaceTimeOutput(double t, N_Vector* y, FILE* output, UserData data) +{ + double *ydata,*psidata; + ydata = N_VGetArrayPointer_OpenMP(*y); + + if(data->adaptiveGrid){ + psidata = data->grid->x; + }else{ + psidata = data->uniformGrid; + } + + for (size_t i = 0; i < data->npts; i++) { + fprintf(output, "%15.9e\t%15.9e\t",psi(i+1),t); + if(i==0){ + fprintf(output, "%15.9e\t",psi(2)-psi(1)); + } + else{ + fprintf(output, "%15.6e\t",psi(i+1)-psi(i)); + } + for (size_t j = 0; j < data->nvar; j++) { + if(j!=data->nvar-1){ + fprintf(output, "%15.9e\t",ydata[j+i*data->nvar]); + }else{ + fprintf(output, "%15.9e",ydata[j+i*data->nvar]); + } + } + fprintf(output, "\n"); + } + fprintf(output, "\n"); +} + +void writeRestart(double t, N_Vector* y, N_Vector* ydot, FILE* output, UserData data){ + double *ydata,*psidata, *ydotdata; + ydata = N_VGetArrayPointer_OpenMP(*y); + ydotdata = N_VGetArrayPointer_OpenMP(*ydot); + if(data->adaptiveGrid){ + psidata = data->grid->x; + }else{ + psidata = data->uniformGrid; + } + fwrite(&t, sizeof(t), 1, output); //write time + fwrite(psidata, data->npts*sizeof(psidata), 1, output); //write grid + fwrite(ydata, data->neq*sizeof(ydata), 1, output); //write solution + fwrite(ydotdata, data->neq*sizeof(ydotdata), 1, output); //write solutiondot +} + +void readRestart(N_Vector* y, N_Vector* ydot, FILE* input, UserData data){ + double *ydata,*psidata, *ydotdata; + double t; + if(data->adaptiveGrid){ + psidata = data->grid->x; + }else{ + psidata = data->uniformGrid; + } + ydata = N_VGetArrayPointer_OpenMP(*y); + ydotdata = N_VGetArrayPointer_OpenMP(*ydot); + fread(&t, sizeof(t), 1, input); + data->tNow=t; + fread(psidata, data->npts*sizeof(psidata), 1, input); + fread(ydata, data->neq*sizeof(ydata), 1, input); + fread(ydotdata, data->neq*sizeof(ydotdata), 1, input); + if(data->adaptiveGrid){ + storeGrid(data->grid->x,data->grid->xOld,data->npts); + } +} + +void printGlobalHeader(UserData data) +{ + fprintf((data->globalOutput), "%8s\t","#Time(s)"); + //fprintf((data->globalOutput), "%15s","S_u(exp*)(m/s)"); + fprintf((data->globalOutput), "%15s","bFlux(kg/m^2/s)"); + fprintf((data->globalOutput), "%16s"," IsothermPos(m)"); + fprintf((data->globalOutput), "%15s\t","Pressure(Pa)"); + fprintf((data->globalOutput), "%15s\t","Pdot(Pa/s)"); + fprintf((data->globalOutput), "%15s\t","gamma"); + fprintf((data->globalOutput), "%15s\t","S_u(m/s)"); + fprintf((data->globalOutput), "%15s\t","Tu(K)"); + fprintf((data->globalOutput), "\n"); +} +// +//void printSpaceTimeRates(double t, N_Vector ydot, UserData data) +//{ +// double *ydotdata,*psidata; +// ydotdata = N_VGetArrayPointer_OpenMP(ydot); +// psidata = N_VGetArrayPointer_OpenMP(data->grid); +// for (int i = 0; i < data->npts; i++) { +// fprintf((data->ratesOutput), "%15.6e\t%15.6e\t",psi(i+1),t); +// for (int j = 0; j < data->nvar; j++) { +// fprintf((data->ratesOutput), "%15.6e\t",ydotdata[j+i*data->nvar]); +// } +// fprintf((data->ratesOutput), "\n"); +// } +// fprintf((data->ratesOutput), "\n\n"); +//} +// +void printGlobalVariables(double t, N_Vector* y, N_Vector* ydot, UserData data) +{ + double *ydata,*ydotdata, *innerMassFractionsData, *psidata; + innerMassFractionsData = data->innerMassFractions; + + if(data->adaptiveGrid){ + psidata = data->grid->x; + }else{ + psidata = data->uniformGrid; + } + + double TAvg, RAvg, YAvg, psiAvg; + ydata = N_VGetArrayPointer_OpenMP(*y); + ydotdata = N_VGetArrayPointer_OpenMP(*ydot); + TAvg=data->isotherm; + double sum=ZERO; + double dpsim,area,aream,drdt; + double Cpb,Cvb,gamma,rho,flameArea,Tu; + + + /*Find the isotherm chosen by the user*/ + size_t j=1; + size_t jj=1; + size_t jjj=1; + double wdot[data->nsp]; + double wdotMax=0.0e0; + double advTerm=0.0e0; + psiAvg=0.0e0; + if(T(data->npts)>T(1)){ + while (T(j)k_oxidizer-1]-Y(data->npts,data->k_oxidizer); + while (fabs((T(jj+1)-T(jj))/T(jj))>1e-08) { + jj=jj+1; + } + + setGas(data,ydata,jj); + Tu=T(jj); + rho=data->gas->density(); + Cpb=data->gas->cp_mass(); //J/kg/K + Cvb=data->gas->cv_mass(); //J/kg/K + gamma=Cpb/Cvb; + + } + else{ + while (T(j)>TAvg) { + j=j+1; + } + YAvg=innerMassFractionsData[data->k_oxidizer-1]-Y(1,data->k_oxidizer); + while (fabs((T(data->npts-jj-1)-T(data->npts-jj))/T(data->npts-jj))>1e-08) { + jj=jj+1; + } + + setGas(data,ydata,data->npts-jj); + Tu=T(data->npts-jj); + rho=data->gas->density(); + Cpb=data->gas->cp_mass(); //J/kg/K + Cvb=data->gas->cv_mass(); //J/kg/K + gamma=Cpb/Cvb; + } + if(T(j)nt, data->nvar, + //// data->grid->x, data->npts); + //nMax=maxGradIndex(ydata, data->nt, data->nvar, + // data->grid->x, data->npts); + //advTerm=(T(nMax)-T(nMax-1))/(data->mass*(psi(nMax)-psi(nMax-1))); + //aream=calc_area(R(nMax),&data->metric); + ////setGas(data,ydata,nMax); + ////rho=data->gas->density(); + //psiAvg=-Tdot(nMax)/(rho*aream*advTerm); + ////if(t>data->ignTime){ + //// for(size_t n=2;nnpts;n++){ + //// setGas(data,ydata,n); + //// data->gas->getNetProductionRates(wdot); //kmol/m^3 + //// advTerm=(T(n)-T(n-1))/(data->mass*(psi(n)-psi(n-1))); + //// if(fabs(wdot[data->k_oxidizer-1])>=wdotMax){ + //// aream=calc_area(R(n),&data->metric); + //// psiAvg=-Tdot(n)/(rho*aream*advTerm); + //// wdotMax=fabs(wdot[data->k_oxidizer-1]); + //// } + //// } + ////} + ////else{ + //// psiAvg=0.0e0; + ////} + + + //drdt=(RAvg-data->flamePosition[1])/(t-data->flameTime[1]); + //data->flamePosition[0]=data->flamePosition[1]; + //data->flamePosition[1]=RAvg; + //data->flameTime[0]=data->flameTime[1]; + //data->flameTime[1]=t; + //flameArea=calc_area(RAvg,&data->metric); + + /*Use the Trapezoidal rule to calculate the mass burning rate based on + * the consumption of O2*/ + aream= calc_area(R(1)+1e-03*data->domainLength,&data->metric); + for (j = 2; j npts; j++) { + dpsim=(psi(j)-psi(j-1))*data->mass; + area= calc_area(R(j),&data->metric); + sum=sum+HALF*dpsim*((Ydot(j-1,data->k_oxidizer)/aream) + +(Ydot(j,data->k_oxidizer)/area)); + aream=area; + } + + //double maxOH,maxHO2; + //maxOH=0.0e0; + //maxHO2=0.0e0; + //for(j=1;jnpts;j++){ + // if(Y(j,data->k_OH)>maxOH){ + // maxOH=Y(j,data->k_OH); + // } + //} + //for(j=1;jnpts;j++){ + // if(Y(j,data->k_HO2)>maxHO2){ + // maxHO2=Y(j,data->k_HO2); + // } + //} + + fprintf((data->globalOutput), "%15.6e\t",t); + //fprintf((data->globalOutput), "%15.6e\t",psiAvg); + fprintf((data->globalOutput), "%15.6e\t",fabs(sum)/YAvg); + fprintf((data->globalOutput), "%15.6e\t",RAvg); + fprintf((data->globalOutput), "%15.6e\t",P(data->npts)); + fprintf((data->globalOutput), "%15.6e\t",Pdot(data->npts)); + fprintf((data->globalOutput), "%15.6e\t",gamma); + fprintf((data->globalOutput), "%15.6e\t",fabs(sum)/(YAvg*rho)); + fprintf((data->globalOutput), "%15.6e\t",Tu); + fprintf((data->globalOutput), "\n"); +} + +// +//void printSpaceTimeOutputInterpolated(double t, N_Vector y, UserData data) +//{ +// double *ydata,*psidata; +// ydata = N_VGetArrayPointer_OpenMP(y); +// psidata = N_VGetArrayPointer_OpenMP(data->grid); +// for (int i = 0; i < data->npts; i++) { +// fprintf((data->gridOutput), "%15.6e\t%15.6e\t",psi(i+1),t); +// for (int j = 0; j < data->nvar; j++) { +// fprintf((data->gridOutput), "%15.6e\t",ydata[j+i*data->nvar]); +// } +// fprintf((data->gridOutput), "\n"); +// } +// fprintf((data->gridOutput), "\n\n"); +//} +// +// +////void repairSolution(N_Vector y, N_Vector ydot, UserData data){ +//// int npts=data->npts; +//// double *ydata; +//// double *ydotdata; +//// ydata = N_VGetArrayPointer_OpenMP(y); +//// ydotdata = N_VGetArrayPointer_OpenMP(ydot); +//// +//// T(2)=T(1); +//// T(npts-1)=T(npts); +//// Tdot(2)=Tdot(1); +//// Tdot(npts-1)=Tdot(npts); +//// for (int k = 1; k <=data->nsp; k++) { +//// Y(2,k)=Y(1,k); +//// Y(npts-1,k)=Y(npts,k); +//// +//// Ydot(2,k)=Ydot(1,k); +//// Ydot(npts-1,k)=Ydot(npts,k); +//// } +////} diff --git a/residue.h b/residue.h new file mode 100644 index 0000000..6222fc9 --- /dev/null +++ b/residue.h @@ -0,0 +1,90 @@ +#ifndef SUNDIALS_DEF +#define SUNDIALS_DEF +#include +#include +#endif + +#ifndef PRINT_DEF +#define PRINT_DEF +#include //for strings +#include //for printf,scanf +#include //for atoi, atof +#endif + +#ifndef CANTERA_DEF +#define CANTERA_DEF +#include +#include +#endif + +#include "UserData.h" + + +double maxGradPosition(const double* y, const size_t nt, + const size_t nvar, const double* x, size_t nPts); + +int maxGradIndex(const double* y, const size_t nt, + const size_t nvar, const double* x, size_t nPts); + +double maxCurvPosition(const double* y, const size_t nt, + const size_t nvar, const double* x, size_t nPts); + +int maxCurvIndex(const double* y, const size_t nt, + const size_t nvar, const double* x, size_t nPts); + +double isothermPosition(const double* y, const double T, const size_t nt, + const size_t nvar, const double* x, const size_t nPts); + +int setAlgebraicVariables(N_Vector *id,UserData data); + +inline double calc_area(double x,int* i); + +void updateSolution(double* y, double* ydot, const size_t nvar, + const double xOld[],const double xNew[],const size_t npts); + +void readInitialCondition(FILE* input, double* ydata, const size_t nvar, const size_t nr, const size_t nPts); + +double systemMass(double* ydata, UserData data); + +int initializePsiGrid(double* ydata, double* psidata, UserData data); + +int setInitialCondition(N_Vector* y, + N_Vector* ydot, + UserData data); + + +inline void setGas(UserData data, double *ydata, size_t gridPoint); + +void getTransport(UserData data, + double *ydata, + size_t gridPoint, + double *rho, + double *lambda, + double *YV); + +int residue(double t, + N_Vector y, + N_Vector ydot, + N_Vector res, + void *user_data); + +void trackFlameOH(N_Vector y,UserData data); +void trackFlame(N_Vector y,UserData data); +size_t BathGasIndex(UserData data); +size_t oxidizerIndex(UserData data); + +inline double Qdot(double* t, + double* x, + double* ignTime, + double* kernelSize, + double* maxQdot); + +void printSpaceTimeHeader(UserData data); +void printSpaceTimeOutput(double t, N_Vector* y, FILE* output, UserData data); +void printSpaceTimeRates(double t, N_Vector ydot, UserData data); +void printGlobalHeader(UserData data); +void printGlobalVariables(double t, N_Vector* y, N_Vector* ydot, UserData data); +void printSpaceTimeOutputInterpolated(double t, N_Vector y, UserData data); + +void writeRestart(double t, N_Vector* y, N_Vector* ydot, FILE* output, UserData data); +void readRestart(N_Vector* y, N_Vector* ydot, FILE* input, UserData data); diff --git a/restart/Makefile b/restart/Makefile new file mode 100644 index 0000000..f8f0a04 --- /dev/null +++ b/restart/Makefile @@ -0,0 +1,6 @@ +all: write.out read.out + +write.out: write.c + gcc write.c -o write.out +read.out: read.c + gcc read.c -o read.out diff --git a/restart/read.c b/restart/read.c new file mode 100644 index 0000000..1a63e5c --- /dev/null +++ b/restart/read.c @@ -0,0 +1,18 @@ +#include + +int main(){ + + size_t N=10; + double a[N]; + + FILE *fp; + fp=fopen("restart.bin","r"); + fread(a,sizeof(a),1,fp); + fclose(fp); + + for (int i = 0; i < N; i++) { + printf("%15.6e\n", a[i]); + } + + return(0); +} diff --git a/restart/write.c b/restart/write.c new file mode 100644 index 0000000..26cbe77 --- /dev/null +++ b/restart/write.c @@ -0,0 +1,20 @@ +#include + +int main(){ + + size_t N=10; + double L=1.0e0; + double a[N]; + double dx=L/((double)N - 1.0e0); + + for (int i = 0; i < N; i++) { + a[i]=(double)i*dx; + } + + FILE *fp; + fp=fopen("restart.bin","w"); + fwrite(a, sizeof(a), 1, fp); + fclose(fp); + + return(0); +} diff --git a/solution.cpp b/solution.cpp new file mode 100644 index 0000000..9e13b06 --- /dev/null +++ b/solution.cpp @@ -0,0 +1,90 @@ +#include "solution.h" +int allocateSolution(size_t neq, + int nThreads, + N_Vector *y, + N_Vector *ydot, + N_Vector *res, + N_Vector *id, + N_Vector *atolv, + N_Vector *constraints){ + + + *y = N_VNew_OpenMP(neq,nThreads); + if(*y==NULL){ + printf("Allocation Failed!\n"); + return(1); + } + N_VConst((realtype)(0.0e0), *y); + + *ydot = N_VNew_OpenMP(neq,nThreads); + if(*ydot==NULL){ + printf("Allocation Failed!\n"); + return(1); + } + N_VConst((realtype)(0.0e0), *ydot); + + *res = N_VNew_OpenMP(neq,nThreads); + if(*res==NULL){ + printf("Allocation Failed!\n"); + return(1); + } + N_VConst((realtype)(0.0e0), *res); + + *id = N_VNew_OpenMP(neq,nThreads); + if(*id==NULL){ + printf("Allocation Failed!\n"); + return(1); + } + N_VConst((realtype)(1.0e0), *id); + + *atolv = N_VNew_OpenMP(neq,nThreads); + if(*atolv==NULL){ + printf("Allocation Failed!\n"); + return(1); + } + N_VConst((realtype)(0.0e0), *atolv); + + *constraints = N_VNew_OpenMP(neq,nThreads); + if(*constraints==NULL){ + printf("Allocation Failed!\n"); + return(1); + } + N_VConst((realtype)(0.0e0), *constraints); + + return(0); +} + +void freeSolution(N_Vector *y, + N_Vector *ydot, + N_Vector *res, + N_Vector *id, + N_Vector *atolv, + N_Vector *constraints){ + + if(*y!=NULL){ + N_VDestroy_OpenMP(*y); + printf("y Destroyed!\n"); + } + if(*ydot!=NULL){ + N_VDestroy_OpenMP(*ydot); + printf("ydot Destroyed!\n"); + } + if(*res!=NULL){ + N_VDestroy_OpenMP(*res); + printf("res Destroyed!\n"); + } + if(*id!=NULL){ + N_VDestroy_OpenMP(*id); + printf("id Destroyed!\n"); + } + if(*atolv!=NULL){ + N_VDestroy_OpenMP(*atolv); + printf("atolv Destroyed!\n"); + } + + if(*constraints!=NULL){ + N_VDestroy_OpenMP(*constraints); + printf("constraints Destroyed!\n"); + } + printf("\n\n"); +} diff --git a/solution.h b/solution.h new file mode 100644 index 0000000..7f220c3 --- /dev/null +++ b/solution.h @@ -0,0 +1,15 @@ +#ifndef PRINT_DEF +#define PRINT_DEF +#include //for strings +#include //for printf,scanf +#include //for atoi, atof +#endif + +#ifndef SUNDIALS_DEF +#define SUNDIALS_DEF +#include +#include +#endif + +int allocateSolution(size_t neq, int nThreads, N_Vector *y, N_Vector *ydot, N_Vector *res, N_Vector *id, N_Vector *atolv, N_Vector *constraints); +void freeSolution(N_Vector *y, N_Vector *ydot, N_Vector *res, N_Vector *id, N_Vector *atolv, N_Vector *constraints); diff --git a/timing.h b/timing.h new file mode 100644 index 0000000..b65b3d3 --- /dev/null +++ b/timing.h @@ -0,0 +1,2 @@ +double get_wall_time(); +double get_cpu_time(); diff --git a/timing.hpp b/timing.hpp new file mode 100644 index 0000000..e07e3eb --- /dev/null +++ b/timing.hpp @@ -0,0 +1,45 @@ +// Windows +#ifdef _WIN32 +#include +double get_wall_time(){ + LARGE_INTEGER time,freq; + if (!QueryPerformanceFrequency(&freq)){ + // Handle error + return 0; + } + if (!QueryPerformanceCounter(&time)){ + // Handle error + return 0; + } + return (double)time.QuadPart / freq.QuadPart; +} +double get_cpu_time(){ + FILETIME a,b,c,d; + if (GetProcessTimes(GetCurrentProcess(),&a,&b,&c,&d) != 0){ + // Returns total user time. + // Can be tweaked to include kernel times as well. + return + (double)(d.dwLowDateTime | + ((unsigned long long)d.dwHighDateTime << 32)) * 0.0000001; + }else{ + // Handle error + return 0; + } +} + +// Posix/Linux +#else +#include +#include +double get_wall_time(){ + struct timeval time; + if (gettimeofday(&time,NULL)){ + // Handle error + return 0; + } + return (double)time.tv_sec + (double)time.tv_usec * .000001; +} +double get_cpu_time(){ + return (double)clock() / CLOCKS_PER_SEC; +} +#endif