/** * @file StFlow.cpp */ /* * $Author$ * $Revision$ * $Date$ */ // Copyright 2002 California Institute of Technology // turn off warnings under Windows #ifdef WIN32 #pragma warning(disable:4786) #pragma warning(disable:4503) #endif #include #include #include "StFlow.h" #include "../ArrayViewer.h" #include "ctml.h" #include "MultiJac.h" using namespace ctml; namespace Cantera { //------------------- importSolution ------------------------ /** * Import a previous solution to use as an initial estimate. The * previous solution may have been computed using a different * reaction mechanism. Species in the old and new mechanisms are * matched by name, and any species in the new mechanism that were * not in the old one are set to zero. The new solution is created * with the same number of grid points as in the old solution. */ void importSolution(int points, doublereal* oldSoln, igthermo_t& oldmech, int size_new, doublereal* newSoln, igthermo_t& newmech) { // Number of components in old and new solutions int nv_old = oldmech.nSpecies() + 4; int nv_new = newmech.nSpecies() + 4; if (size_new < nv_new*points) { throw CanteraError("importSolution", "new solution array must have length "+ int2str(nv_new*points)); } int n, j, knew; string nm; // copy u,V,T,lambda for (j = 0; j < points; j++) for (n = 0; n < 4; n++) newSoln[nv_new*j + n] = oldSoln[nv_old*j + n]; // copy mass fractions int nsp0 = oldmech.nSpecies(); int nsp1 = newmech.nSpecies(); // loop over the species in the old mechanism for (int k = 0; k < nsp0; k++) { nm = oldmech.speciesName(k); // name // location of this species in the new mechanism. // If < 0, then the species is not in the new mechanism. knew = newmech.speciesIndex(nm); // copy this species from the old to the new solution vectors if (knew >= 0) { for (j = 0; j < points; j++) { newSoln[nv_new*j + 4 + knew] = oldSoln[nv_old*j + 4 + k]; } } } // normalize mass fractions for (j = 0; j < points; j++) { newmech.setMassFractions(&newSoln[nv_new*j + 4]); newmech.getMassFractions(nsp1,&newSoln[nv_new*j + 4]); } } //---------------------- drawline ---------------------------------- inline void drawline(ostream& s) { s << "\n-------------------------------------" << "------------------------------------------"; } //--------------------- linear interp ------------------------------ StFlow::StFlow(igthermo_t* ph, int nsp, int points) : Resid1D(nsp+4, points), m_inlet_u(0.0), m_inlet_V(0.0), m_inlet_T(-1.0), m_surface_T(-1.0), m_press(-1.0), m_nsp(nsp), m_thermo(0), m_kin(0), m_trans(0), m_jac(0), m_ok(false), m_do_soret(false), m_transport_option(-1), m_efctr(0.0) { m_type = cFlowType; m_boundary.resize(2,0); m_points = points; m_thermo = ph; if (ph == 0) return; // used to create a dummy object int nsp2 = m_thermo->nSpecies(); if (nsp2 != m_nsp) { m_nsp = nsp2; Resid1D::resize(m_nsp+4, points); } // make a local copy of the species molecular weight vector m_wt = m_thermo->molecularWeights(); // the species mass fractions are the last components in the solution // vector, so the total number of components is the number of species // plus the offset of the first mass fraction. m_nv = c_offset_Y + m_nsp; // enable all species equations by default m_do_species.resize(m_nsp, true); // but turn off the energy equation at all points m_do_energy.resize(m_points,false); m_diff.resize(m_nsp,m_points); m_flux.resize(m_nsp,m_points); m_wdot.resize(m_nsp,m_points, 0.0); m_surfdot.resize(m_nsp, 0.0); m_ybar.resize(m_nsp); //-------------- default solution bounds -------------------- vector_fp vmin(m_nv), vmax(m_nv); // no bounds on u vmin[0] = -1.e20; vmax[0] = 1.e20; // no negative V vmin[1] = -0.1; vmax[1] = 1.e20; // temperature bounds vmin[2] = 200.0; vmax[2]= 1.e9; // lamda should be negative vmin[3] = -1.e20; vmax[3] = 1.0; // mass fraction bounds int k; for (k = 0; k < m_nsp; k++) { vmin[4+k] = -1.0e-5; vmax[4+k] = 1.1; } setBounds(vmin.size(), vmin.begin(), vmax.size(), vmax.begin()); //-------------------- default error tolerances ---------------- vector_fp rtol(m_nv, 1.0e-8); vector_fp atol(m_nv, 1.0e-15); setTolerances(rtol.size(), rtol.begin(), atol.size(), atol.begin()); //-------------------- grid refinement ------------------------- m_refiner->setActive(0, false); m_refiner->setActive(1, false); m_refiner->setActive(2, false); m_refiner->setActive(3, false); } /** * Change the grid size. Called after grid refinement. */ void StFlow::resize(int points) { Resid1D::resize(m_nv, points); m_rho.resize(m_points, 0.0); m_wtm.resize(m_points, 0.0); m_cp.resize(m_points, 0.0); m_enth.resize(m_points, 0.0); m_visc.resize(m_points, 0.0); m_tcon.resize(m_points, 0.0); m_diff.resize(m_nsp,m_points); m_flux.resize(m_nsp,m_points); m_wdot.resize(m_nsp,m_points, 0.0); m_do_energy.resize(m_points,false); m_fixedy.resize(m_nsp, m_points); m_fixedtemp.resize(m_points); m_dz.resize(m_points-1); m_z.resize(m_points); } void StFlow::setupGrid(int n, const doublereal* z) { resize(n); int j; m_z[0] = z[0]; for (j = 1; j < m_points; j++) { m_z[j] = z[j]; m_dz[j-1] = m_z[j] - m_z[j-1]; } } /** * Install a transport manager. */ void StFlow::setTransport(Transport& trans, bool withSoret) { m_trans = &trans; m_do_soret = withSoret; if (m_trans->model() == cMulticomponent) { m_transport_option = c_Multi_Transport; } else if (m_trans->model() == cMixtureAveraged) { m_transport_option = c_Mixav_Transport; if (withSoret) throw CanteraError("setTransport", "Thermal diffusion (the Soret effect) " "requires using a multicomponent transport model."); } else throw CanteraError("setTransport","unknown transport model."); } /** * Set the gas object state to be consistent with the solution at * point j. */ void StFlow::setGas(const doublereal* x,int j) { m_thermo->setTemperature(T(x,j)); const doublereal* yy = x + m_nv*j + c_offset_Y; m_thermo->setMassFractions_NoNorm(yy); m_thermo->setPressure(m_press); } /** * Set the gas state to be consistent with the solution at the * midpoint between j and j + 1. */ void StFlow::setGasAtMidpoint(const doublereal* x,int j) { m_thermo->setTemperature(0.5*(T(x,j)+T(x,j+1))); const doublereal* yyj = x + m_nv*j + c_offset_Y; const doublereal* yyjp = x + m_nv*(j+1) + c_offset_Y; for (int k = 0; k < m_nsp; k++) m_ybar[k] = 0.5*(yyj[k] + yyjp[k]); m_thermo->setMassFractions_NoNorm(m_ybar.begin()); m_thermo->setPressure(m_press); } /** * Evaluate the residual function for axisymmetric stagnation * flow. If jpt is less than zero, the residual function is * evaluated at all grid points. If jpt >= 0, then the residual * function is only evaluated at grid points jpt-1, jpt, and * jpt+1. This option is used to efficiently evaluate the * Jacobian numerically. * */ void AxiStagnFlow::eval(int jg, doublereal* xg, doublereal* rg, integer* diagg, doublereal rdt) { // if evaluating a Jacobian, and the global point is outside // the domain of influence for this domain, then skip // evaluating the residual if (jg >=0 && (jg < firstPoint() - 1 || jg > lastPoint() + 1)) return; // if evaluating a Jacobian, compute the steady-state residual if (jg >= 0) rdt = 0.0; // start of local part of global arrays doublereal* x = xg + loc(); doublereal* rsd = rg + loc(); integer* diag = diagg + loc(); int jmin, jmax, jpt; jpt = jg - firstPoint(); if (jg < 0) { // evaluate all points jmin = 0; jmax = m_points - 1; } else { // evaluate points for Jacobian jmin = max(jpt-1, 0); jmax = min(jpt+1,m_points-1); } // properties are computed for grid points from j0 to j1 int j0 = max(jmin-1,0); int j1 = min(jmax+1,m_points-1); int j, k; //----------------------------------------------------- // update properties //----------------------------------------------------- // thermodynamic properties only if a Jacobian is // not being evaluated if (jpt < 0) updateThermo(x, j0, j1); // update transport properties only if a Jacobian is // not being evaluated if (jpt < 0) updateTransport(x, j0, j1); // update the species diffusive mass fluxes whether or not a // Jacobian is being evaluated updateDiffFluxes(x, j0, j1); //---------------------------------------------------- // evaluate the residual equations at all required // grid points //---------------------------------------------------- doublereal sum, sum2, dtdzj; for (j = jmin; j <= jmax; j++) { //---------------------------------------------- // left boundary //---------------------------------------------- if (j == 0) { // these may be modified by a boundary object #define NEW_INLET #ifdef NEW_INLET // Continuity. This propagates information right-to-left, // since rho_u at point 0 is dependent on rho_u at point 1, // but not on mdot from the inlet. rsd[index(c_offset_U,0)] = -(rho_u(x,1) - rho_u(x,0))/m_dz[0] -(density(1)*V(x,1) + density(0)*V(x,0)); // the inlet (or other) object connected to this one // will modify these equations by subtracting its values // for V, T, and mdot. As a result, these residual equations // will force the solution variables to the values for // the boundary object rsd[index(c_offset_V,0)] = V(x,0); rsd[index(c_offset_T,0)] = T(x,0); rsd[index(c_offset_L,0)] = -rho_u(x,0); //cout << "density = " << density(0) << " " << u(x,0) // << " " << rho_u(x,0) << endl; // The default boundary condition for species is zero // flux. However, the boundary object may modify // this. for (k = 0; k < m_nsp; k++) { rsd[index(c_offset_Y + k, 0)] = -(m_flux(k,0) + rho_u(x,0)* Y(x,k,0)); } #else // first, call the left boundary object to evaluate // the residual m_boundary[0]->eval(x + index(0,0), m_rho[0], m_flux.begin(), rsd + index(0,0)); // Now modify the left boundary conditions to allow // specifying the mass flux at both boundaries. The // right mass flux is specified directly as a boundary // condition on the continuity equation; the left mass // flux is matched by adjusting lambda. // Shift the left continuity boundary condition to lambda, rsd[index(c_offset_L, 0)] = rsd[index(c_offset_U, 0)]; // and replace it with the continuity equation. rsd[index(c_offset_U,0)] = -(rho_u(x,1) - rho_u(x,0))/m_dz[0] -(density(1)*V(x,1) + density(0)*V(x,0)); #endif } //---------------------------------------------- // // right boundary // //---------------------------------------------- else if (j == m_points - 1) { // the boundary object connected to the right of this // one may modify these equations by subtracting its // values for V, T, and mdot. As a result, these // residual equations will force the solution // variables to the values for the boundary object rsd[index(0,j)] = rho_u(x,j); rsd[index(1,j)] = V(x,j); rsd[index(2,j)] = T(x,j); doublereal sum = 0.0; for (k = 0; k < m_nsp; k++) { sum += Y(x,k,j); rsd[index(k+4,j)] = rho_u(x,j)*Y(x,k,j) + m_flux(k,j-1); } // TODO: why is this done here, but not for the left // boundary or interior? rsd[index(4,j)] = 1.0 - sum; diag[index(4,j)] = 0; } //------------------------------------------ // interior points //------------------------------------------ else { //---------------------------------------------- // Continuity equation // // Note that this propagates the mass flow rate // information to the left (j+1 -> j) from the // value specified at the right boundary. The // lambda information propagates in the opposite // direction. // // d(\rho u)/dz + 2\rho V = 0 // //------------------------------------------------ rsd[index(c_offset_U,j)] = -(rho_u(x,j+1) - rho_u(x,j))/m_dz[j] -(density(j+1)*V(x,j+1) + density(j)*V(x,j)); //------------------------------------------------ // Radial momentum equation // // \rho u dV/dz + \rho V^2 = d(\mu dV/dz)/dz - lambda // //------------------------------------------------- rsd[index(c_offset_V,j)] = (shear(x,j) - lambda(x,j) - rho_u(x,j)*dVdz(x,j) - m_rho[j]*V(x,j)*V(x,j))/m_rho[j]; //------------------------------------------------- // Species equations // // \rho u dY_k/dz + dJ_k/dz + M_k\omega_k // //------------------------------------------------- getWdot(x,j); doublereal convec, diffus; for (k = 0; k < m_nsp; k++) { if (m_do_species[k]) { convec = rho_u(x,j)*dYdz(x,k,j); diffus = 2.0*(m_flux(k,j) - m_flux(k,j-1)) /(z(j+1) - z(j-1)); rsd[index(c_offset_Y + k, j)] = (m_wt[k]*(wdot(k,j) ) - convec - diffus)/m_rho[j] - rdt*(Y(x,k,j) - Y_prev(k,j)); diag[index(c_offset_Y + k, j)] = 1; } } //----------------------------------------------- // energy equation //----------------------------------------------- if (m_do_energy[j]) { setGas(x,j); // heat release term const vector_fp& h_RT = m_thermo->enthalpy_RT(); const vector_fp& cp_R = m_thermo->cp_R(); sum = 0.0; sum2 = 0.0; doublereal flxk; for (k = 0; k < m_nsp; k++) { flxk = 0.5*(m_flux(k,j-1) + m_flux(k,j)); sum += wdot(k,j)*h_RT[k]; sum2 += flxk*cp_R[k]/m_wt[k]; } sum *= GasConstant * T(x,j); dtdzj = dTdz(x,j); sum2 *= GasConstant * dtdzj; rsd[index(c_offset_T, j)] = - m_cp[j]*rho_u(x,j)*dtdzj - divHeatFlux(x,j) - sum - sum2; rsd[index(c_offset_T, j)] /= (m_rho[j]*m_cp[j]); rsd[index(c_offset_T, j)] = rsd[index(c_offset_T, j)] + m_efctr*(T_fixed(j) - T(x,j)); rsd[index(c_offset_T, j)] -= rdt*(T(x,j) - T_prev(j)); diag[index(c_offset_T, j)] = 1; } } // residual equations if the energy or species equations // are disabled for (k = 0; k < m_nsp; k++) { if (!m_do_species[k]) { rsd[index(c_offset_Y+k,j)] = Y(x,k,j) - Y_fixed(k,j); diag[index(c_offset_Y+k, j)] = 0; } } if (!m_do_energy[j]) { rsd[index(c_offset_T, j)] = T(x,j) - T_fixed(j); diag[index(c_offset_T, j)] = 0; } // lambda if (j > 0) { rsd[index(c_offset_L, j)] = lambda(x,j) - lambda(x,j-1); diag[index(c_offset_L, j)] = 0; } } } /** * Update the transport properties at grid points in the range * from j0 to j1, based on solution x. */ void AxiStagnFlow::updateTransport(doublereal* x,int j0, int j1) { int j; //for (j = j0; j <= j1; j++) { for (j = j0; j < j1; j++) { setGasAtMidpoint(x,j); m_visc[j] = m_trans->viscosity(); m_trans->getMixDiffCoeffs(&m_diff(0,j)); m_tcon[j] = m_trans->thermalConductivity(); } } void OneDFlow::eval(int jg, doublereal* xg, doublereal* rg, integer* diagg, doublereal rdt) { static double elapsed; // doublereal rtau = 1.e5; clock_t t0 = clock(); // doublereal rdt_save = rdt; if (jg >= 0) rdt = 0.0; if (jg >= 0 && (jg < firstPoint() || jg > lastPoint())) return; // start of local part of global arrays doublereal* x = xg + loc(); doublereal* rsd = rg + loc(); integer* diag = diagg + loc(); int jmin, jmax, jpt; jpt = jg - firstPoint(); for (int jj = 0; jj < m_points*m_nv; jj++) { if (x[jj] < -1.e20 || x[jj] > 1.e20) { showSolution(cout, x); throw CanteraError("tlt","tlt"); } } // the residual function is evaluated for jmin <= j <= jmax, and // properties and evaluated for j0 <= j <= j1. if (jg < 0) { jmin = 0; jmax = m_points - 1; } else { jmin = max(jpt-1,0); jmax = min(jpt+1,m_points-1); } int j0 = max(jmin-1,0); int j1 = min(jmax+1,m_points-1); int j, k; //----------------------------------------------------- // compute properties needed in the residual equations //----------------------------------------------------- // for each point, synchronize the state of the fluid object // with the current solution values, and then use this object // to compute the density, mean molecular weight, and mean // specific heat at constant pressure. if (jpt < 0) updateThermo(x, j0, j1); // skip updating transport properties if a Jacobian is // being evaluated if (jpt < 0) updateTransport(x, j0, j1); // update the species diffusive mass fluxes updateDiffFluxes(x, j0, j1); //---------------------------------------------------- // evaluate the residual equations at all required // grid points //---------------------------------------------------- doublereal sum, sum2, deltaz, dtdzj; for (j = jmin; j <= jmax; j++) { //---------------------------------------------- // boundaries //---------------------------------------------- if (j == 0) { setGas(x,0); m_boundary[0]->eval(x, m_rho[0], m_flux.begin(), rsd); } else if (j == m_points - 1) { m_boundary[1]->eval(x + index(0, j), m_rho[j], m_flux.begin() + m_nsp*(j-1), rsd + index(0, j)); } //------------------------------------------ // interior points //------------------------------------------ else { // continuity rsd[index(c_offset_U,j)] = (rho_u(x,j-1) - rho_u(x,j)); // radial velocity = 0 rsd[index(c_offset_V,j)] = V(x,j); // species equations getWdot(x,j); doublereal convec, diffus; for (k = 0; k < m_nsp; k++) { if (m_do_species[k]) { convec = rho_u(x,j) * dYdz(x,k,j); diffus = 2.0*(m_flux(k,j) - m_flux(k,j-1))/(z(j+1) - z(j-1)); rsd[index(c_offset_Y + k, j)] = (m_wt[k]*wdot(k,j) - convec - diffus)/m_rho[j] - rdt*(Y(x,k,j) - Y_prev(k,j)); diag[index(c_offset_Y + k, j)] = 1; } else rsd[index(c_offset_Y+k,j)] = (Y(x,k,j) - Y_fixed(k,j)); } // energy equation if (m_do_energy[j]) { setGas(x,j); // heat release term const vector_fp& h_RT = m_thermo->enthalpy_RT(); const vector_fp& cp_R = m_thermo->cp_R(); sum = 0.0; sum2 = 0.0; deltaz = (z(j+1) - z(j-1)); doublereal flxk; for (k = 0; k < m_nsp; k++) { flxk = 0.5*(m_flux(k,j-1) + m_flux(k,j)); sum += wdot(k,j)*h_RT[k]; sum2 += flxk*cp_R[k]/m_wt[k]; } sum *= GasConstant * T(x,j); dtdzj = (T(x,j+1) - T(x,j-1))/deltaz; // dTdz(x,j) + (m_dz[j-1]/deltaz)*(dTdz(x,j+1) - dTdz(x,j)); sum2 *= GasConstant * dtdzj; rsd[index(c_offset_T, j)] = - m_cp[j]*rho_u(x,j)*dtdzj - divHeatFlux(x,j) - sum - sum2; rsd[index(c_offset_T, j)] /= (m_rho[j]*m_cp[j]); rsd[index(c_offset_T, j)] -= rdt*(T(x,j) - T_prev(j)); diag[index(c_offset_T, j)] = 1; } // lambda = 0 rsd[index(c_offset_L, j)] = lambda(x,j); } for (k = 0; k < m_nsp; k++) { if (!m_do_species[k]) { rsd[index(c_offset_Y+k,j)] = (Y(x,k,j) - Y_fixed(k,j)); diag[index(c_offset_Y+k, j)] = 0; } } if (!m_do_energy[j]) { rsd[index(c_offset_T, j)] = (T(x,j) - T_fixed(j)); diag[index(c_offset_T, j)] = 0; } } clock_t t1 = clock(); elapsed += double(t1 - t0)/CLOCKS_PER_SEC; } /** * Update the transport properties at grid points in the range * from j0 to j1, based on solution x. */ void OneDFlow::updateTransport(doublereal* x,int j0, int j1) { int j; for (j = j0; j < j1; j++) { setGasAtMidpoint(x,j); m_trans->getMixDiffCoeffs(&m_diff(0,j)); m_tcon[j] = m_trans->thermalConductivity(); } } /** * Print the solution. */ void StFlow::showSolution(ostream& s, const doublereal* x) { int nn = m_nv/5; int i, j, n; char* buf = new char[100]; // The mean molecular weight is needed to convert updateThermo(x, 0, m_points-1); for (i = 0; i < nn; i++) { drawline(s); sprintf(buf, "\n z "); s << buf; for (n = 0; n < 5; n++) { sprintf(buf, " %10s ",componentName(i*5 + n).c_str()); s << buf; } drawline(s); for (j = 0; j < m_points; j++) { sprintf(buf, "\n %10.4g ",m_z[j]); s << buf; for (n = 0; n < 5; n++) { sprintf(buf, " %10.4g ",component(x, i*5+n,j)); s << buf; } } s << endl; } int nrem = m_nv - 5*nn; drawline(s); sprintf(buf, "\n z "); s << buf; for (n = 0; n < nrem; n++) { sprintf(buf, " %10s ", componentName(nn*5 + n).c_str()); s << buf; } drawline(s); for (j = 0; j < m_points; j++) { sprintf(buf, "\n %10.4g ",m_z[j]); s << buf; for (n = 0; n < nrem; n++) { sprintf(buf, " %10.4g ",component(x, nn*5+n,j)); s << buf; } } s << endl; } /** * Update the diffusive mass fluxes. */ void StFlow::updateDiffFluxes(const doublereal* x, int j0, int j1) { int j, k; double sum, wtm, rho, dz; switch (m_transport_option) { case c_Mixav_Transport: for (j = j0; j < j1; j++) { sum = 0.0; wtm = m_wtm[j]; rho = density(j); dz = z(j+1) - z(j); for (k = 0; k < m_nsp; k++) { m_flux(k,j) = m_wt[k]*(rho*m_diff(k,j)/wtm); m_flux(k,j) *= (X(x,k,j) - X(x,k,j+1))/dz; sum -= m_flux(k,j); } // correction flux to insure that \sum_k Y_k j_k = 0. for (k = 0; k < m_nsp; k++) m_flux(k,j) += sum*Y(x,k,j); } break; case c_Multi_Transport: cout << " not yet implemented... " << endl; } if (m_do_soret) { cout << " net yet implemented... " << endl; } } void StFlow::outputTEC(ostream &s, const doublereal* x, string title, int zone) { int j,k; s << "TITLE = \"" + title + "\"" << endl; s << "VARIABLES = \"Z (m)\"" << endl; s << "\"u (m/s)\"" << endl; s << "\"V (1/s)\"" << endl; s << "\"T (K)\"" << endl; s << "\"lambda\"" << endl; for (k = 0; k < m_nsp; k++) { s << "\"" << m_thermo->speciesName(k) << "\"" << endl; } s << "ZONE T=\"c" << zone << "\"" << endl; s << " I=" << m_points << ",J=1,K=1,F=POINT" << endl; s << "DT=(SINGLE SINGLE SINGLE SINGLE"; for (k = 0; k < m_nsp; k++) s << " SINGLE"; s << " )" << endl; for (j = 0; j < m_points; j++) { s << z(j) << " "; for (k = 0; k < m_nv; k++) { s << component(x, k, j) << " "; } s << endl; } } string StFlow::componentName(int n) const { switch(n) { case 0: return "u"; case 1: return "V"; case 2: return "T"; case 3: return "lambda"; default: if (n >= (int) c_offset_Y && n < (int) (c_offset_Y + m_nsp)) { return m_thermo->speciesName(n - c_offset_Y); } // if (m_do_species[n - c_offset_Y]) // return m_thermo->speciesName(n - c_offset_Y)+" "; // else // return m_thermo->speciesName(n - c_offset_Y)+" *"; //} else return ""; } } /** * Returns true if all necessary parameters have been set; otherwise it * throws an exception. */ bool StFlow::ready() { if (m_press < 0.0) { throw CanteraError("StFlow::ready", "pressure not specified - call setPressure"); return false; } if (m_points == 0) { throw CanteraError("StFlow::ready", "grid not specified - call setupGrid"); return false; } if (m_nsp < 0) { throw CanteraError("StFlow::ready", "fluid not specified - call specifyFluid"); return false; } if (m_boundary[0] == 0 || m_boundary[1] == 0) { throw CanteraError("StFlow::ready", "boundaries not specified - call setBoundary"); return false; } m_ok = true; return m_ok; } void StFlow::restore(int job, string fname, string id, int& size_z, doublereal* z, int& size_soln, doublereal* soln) { vector ignored; int nsp = m_thermo->nSpecies(); vector_int did_species(nsp, 0); ifstream s(fname.c_str()); if (!s) throw CanteraError("StFlow::restore", "could not open input file "+fname); XML_Node root; root.build(s); s.close(); int k; XML_Node* f = root.findID(id); if (!f) { throw CanteraError("StFlow::restore","No solution with id = "+id); } XML_Node& flow = f->child("flowfield"); f = &flow; //if (f->name() != "flowfield") { // throw CanteraError("StFlow::restore","The element with id " // +id+" does not contain flowfield data."); //} vector str; f->getChildren("string",str); int nstr = str.size(); for (int istr = 0; istr < nstr; istr++) { XML_Node& nd = *str[istr]; writelog(nd["title"]+": "+nd.value()+"\n"); } vector d; f->child("grid_data").getChildren("floatArray",d); int nd = d.size(); vector_fp x; int n, np, j, ks; string nm; bool readgrid = false, wrote_header = false; for (n = 0; n < nd; n++) { XML_Node& fa = *d[n]; nm = fa["title"]; if (nm == "z") { getFloatArray(fa,x,false); np = x.size(); if (job == -1) { size_z = np; //size_soln = (nd - 1)*np; size_soln = (m_nsp + 4)*np; return; } writelog("Grid contains "+int2str(np)+ " points.\n"); if (size_z < np) { throw CanteraError("restore", "grid array must be have length at least " +int2str(np)); } // if (size_soln < (m_nsp + 4)*np) { // throw CanteraError("restore", // "solution array must have length at least " // +int2str((m_nsp + 4)*np)); // } copy(x.begin(), x.end(), z); readgrid = true; } } if (!readgrid) { throw CanteraError("StFlow::restore", "solution contains no grid points."); } cout << "importing...." << endl; writelog("Importing datasets:\n"); for (n = 0; n < nd; n++) { XML_Node& fa = *d[n]; nm = fa["title"]; getFloatArray(fa,x,false); if (nm == "u") { writelog("axial velocity "); if ((int) x.size() == np) { for (j = 0; j < np; j++) { cout << j << " " << x[j] << " " << np << endl; cout << index(0,j) << " " << size_soln << endl; soln[index(0,j)] = x[j]; } } else { cout << "error..." << endl; goto error; } } else if (nm == "z") { ; } else if (nm == "V") { writelog("radial velocity "); if ((int) x.size() == np) { for (j = 0; j < np; j++) soln[index(1,j)] = x[j]; } else goto error; } else if (nm == "T") { writelog("temperature "); if ((int) x.size() == np) { for (j = 0; j < np; j++) soln[index(2,j)] = x[j]; } else goto error; } else if (nm == "L") { writelog("lambda "); if ((int) x.size() == np) { for (j = 0; j < np; j++) soln[index(3,j)] = x[j]; } else goto error; } else if (m_thermo->speciesIndex(nm) >= 0) { writelog(nm+" "); if ((int) x.size() == np) { k = m_thermo->speciesIndex(nm); did_species[k] = 1; for (j = 0; j < np; j++) soln[index(k+4,j)] = x[j]; } } else ignored.push_back(nm); } if (ignored.size() != 0) { writelog("\n\n"); writelog("Ignoring datasets:\n"); int nn = ignored.size(); for (int n = 0; n < nn; n++) { writelog(ignored[n]+" "); } } for (ks = 0; ks < nsp; ks++) { if (did_species[ks] == 0) { if (!wrote_header) { writelog("Missing data for species:\n"); wrote_header = true; } writelog(m_thermo->speciesName(ks)+" "); } } writelog("\n\nFinished importing solution.\n\n"); return; error: throw CanteraError("StFlow::restore","Data size error"); } void StFlow::save(string fname, string id, string desc, doublereal* sol) { int k; struct tm *newtime; time_t aclock; ::time( &aclock ); /* Get time in seconds */ newtime = localtime( &aclock ); /* Convert time to struct tm form */ ArrayViewer soln(m_nv, m_points, sol); XML_Node root("doc"); ifstream fin(fname.c_str()); XML_Node* ct; if (fin) { root.build(fin); XML_Node* same_ID = root.findID(id); int jid = 1; string idnew = id; while (same_ID != 0) { idnew = id + "_" + int2str(jid); jid++; same_ID = root.findID(idnew); } id = idnew; fin.close(); ct = &root.child("ctml"); } else { ct = &root.addChild("ctml"); } XML_Node& flow = (XML_Node&)ct->addChild("flowfield"); flow.addAttribute("type",flowType()); flow.addAttribute("id",id); addString(flow,"timestamp",asctime(newtime)); addFloat(flow, "pressure", m_press, "Pa", "pressure"); // addString(flow,"solve_time",fp2str(m_container->solveTime())); if (desc != "") addString(flow,"description",desc); XML_Node& gv = flow.addChild("grid_data"); addFloatArray(gv,"z",m_z.size(),m_z.begin(), "m","length"); vector_fp x(soln.nColumns()); soln.getRow(0,x.begin()); addFloatArray(gv,"u",x.size(),x.begin(),"m/s","velocity"); soln.getRow(1,x.begin()); addFloatArray(gv,"V", x.size(),x.begin(),"1/s","strainrate"); soln.getRow(2,x.begin()); addFloatArray(gv,"T",x.size(),x.begin(),"K","temperature",0.0); soln.getRow(3,x.begin()); addFloatArray(gv,"L",x.size(),x.begin(),"N/m^4"); for (k = 0; k < m_nsp; k++) { soln.getRow(4+k,x.begin()); addFloatArray(gv,m_thermo->speciesName(k), x.size(),x.begin(),"","massFraction",0.0,1.0); } // XML_Node& inlt = flow.addChild("inlet"); // addFloat(inlt,"T",m_inlet_T,"K","temperature",0.0); // addFloat(inlt,"P",m_press,"Pa","pressure",0.0); // for (k = 0; k < m_nsp; k++) { // if (m_yin[k] != 0.0) // addFloat(inlt, m_thermo->speciesName(k), m_yin[k], // "", "massFraction",0.0,1.0); // } ofstream s(fname.c_str()); if (!s) throw CanteraError("save","could not open file "+fname); ct->writeHeader(s); ct->write(s); s.close(); writelog("Solution saved to file "+fname+" as solution '"+id+"'.\n"); m_container->writeStats(); } void StFlow::save(XML_Node& o, doublereal* sol) { int k; ArrayViewer soln(m_nv, m_points, sol + loc()); XML_Node& flow = (XML_Node&)o.addChild("flowfield"); flow.addAttribute("type",flowType()); flow.addAttribute("id",m_id); if (m_desc != "") addString(flow,"description",m_desc); XML_Node& gv = flow.addChild("grid_data"); addFloat(flow, "pressure", m_press, "Pa", "pressure"); addFloatArray(gv,"z",m_z.size(),m_z.begin(), "m","length"); vector_fp x(soln.nColumns()); soln.getRow(0,x.begin()); addFloatArray(gv,"u",x.size(),x.begin(),"m/s","velocity"); soln.getRow(1,x.begin()); addFloatArray(gv,"V", x.size(),x.begin(),"1/s","rate"); soln.getRow(2,x.begin()); addFloatArray(gv,"T",x.size(),x.begin(),"K","temperature",0.0); soln.getRow(3,x.begin()); addFloatArray(gv,"L",x.size(),x.begin(),"N/m^4"); for (k = 0; k < m_nsp; k++) { soln.getRow(4+k,x.begin()); addFloatArray(gv,m_thermo->speciesName(k), x.size(),x.begin(),"","massFraction",0.0,1.0); } } void StFlow::setJac(MultiJac* jac) { m_jac = jac; } //void StFlow::requestJacUpdate() { // if (m_jac) m_jac->setAge(10000); //} void StFlow::setEnergyFactor(doublereal efctr) { doublereal de = efctr - m_efctr; m_efctr = efctr; int strt = loc(); int jg; for (int j = 1; j < m_points - 1; j++) { jg = strt + index(c_offset_T, j); m_jac->incrementDiagonal(jg, -de); } } }