diff --git a/opm/core/eclipse/CornerpointChopper.hpp b/opm/core/eclipse/CornerpointChopper.hpp new file mode 100644 index 00000000..62a6a05c --- /dev/null +++ b/opm/core/eclipse/CornerpointChopper.hpp @@ -0,0 +1,420 @@ +/* + Copyright 2010 SINTEF ICT, Applied Mathematics. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#ifndef OPM_CORNERPOINTCHOPPER_HEADER_INCLUDED +#define OPM_CORNERPOINTCHOPPER_HEADER_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Dune +{ + + class CornerPointChopper + { + public: + CornerPointChopper(const std::string& file) + : parser_(file, false) + { + for (int dd = 0; dd < 3; ++dd) { + dims_[dd] = parser_.getSPECGRID().dimensions[dd]; + } + + int layersz = 8*dims_[0]*dims_[1]; + const std::vector& ZCORN = parser_.getFloatingPointValue("ZCORN"); + botmax_ = *std::max_element(ZCORN.begin(), ZCORN.begin() + layersz/2); + topmin_ = *std::min_element(ZCORN.begin() + dims_[2]*layersz - layersz/2, + ZCORN.begin() + dims_[2]*layersz); + + abszmax_ = *std::max_element(ZCORN.begin(), ZCORN.end()); + abszmin_ = *std::min_element(ZCORN.begin(), ZCORN.end()); + + std::cout << "Parsed grdecl file with dimensions (" + << dims_[0] << ", " << dims_[1] << ", " << dims_[2] << ")" << std::endl; + } + + + + + const int* dimensions() const + { + return dims_; + } + + + + + const int* newDimensions() const + { + return new_dims_; + } + + + + + const std::pair zLimits() const + { + return std::make_pair(botmax_, topmin_); + } + + const std::pair abszLimits() const + { + return std::make_pair(abszmin_, abszmax_); + } + + + void verifyInscribedShoebox(int imin, int ilen, int imax, + int jmin, int jlen, int jmax, + double zmin, double zlen, double zmax) + { + if (imin < 0) { + std::cerr << "Error! imin < 0 (imin = " << imin << ")\n"; + throw std::runtime_error("Inconsistent user input."); + } + if (ilen > dims_[0]) { + std::cerr << "Error! ilen larger than grid (ilen = " << ilen <<")\n"; + throw std::runtime_error("Inconsistent user input."); + } + if (imax > dims_[0]) { + std::cerr << "Error! imax larger than input grid (imax = " << imax << ")\n"; + throw std::runtime_error("Inconsistent user input."); + } + if (jmin < 0) { + std::cerr << "Error! jmin < 0 (jmin = " << jmin << ")\n"; + throw std::runtime_error("Inconsistent user input."); + } + if (jlen > dims_[1]) { + std::cerr << "Error! jlen larger than grid (jlen = " << jlen <<")\n"; + throw std::runtime_error("Inconsistent user input."); + } + if (jmax > dims_[1]) { + std::cerr << "Error! jmax larger than input grid (jmax = " << jmax << ")\n"; + throw std::runtime_error("Inconsistent user input."); + } + if (zmin < abszmin_) { + std::cerr << "Error! zmin ("<< zmin << ") less than minimum ZCORN value ("<< abszmin_ << ")\n"; + throw std::runtime_error("Inconsistent user input."); + } + if (zmax > abszmax_) { + std::cerr << "Error! zmax ("<< zmax << ") larger than maximal ZCORN value ("<< abszmax_ << ")\n"; + throw std::runtime_error("Inconsistent user input."); + } + if (zlen > (abszmax_ - abszmin_)) { + std::cerr << "Error! zlen ("<< zlen <<") larger than maximal ZCORN (" << abszmax_ << ") minus minimal ZCORN ("<< abszmin_ <<")\n"; + throw std::runtime_error("Inconsistent user input."); + } + } + + void chop(int imin, int imax, int jmin, int jmax, double zmin, double zmax, bool resettoorigin=true) + { + new_dims_[0] = imax - imin; + new_dims_[1] = jmax - jmin; + + // Filter the coord field + const std::vector& COORD = parser_.getFloatingPointValue("COORD"); + int num_coord = COORD.size(); + if (num_coord != 6*(dims_[0] + 1)*(dims_[1] + 1)) { + std::cerr << "Error! COORD size (" << COORD.size() << ") not consistent with SPECGRID\n"; + throw std::runtime_error("Inconsistent COORD and SPECGRID."); + } + int num_new_coord = 6*(new_dims_[0] + 1)*(new_dims_[1] + 1); + double x_correction = COORD[6*((dims_[0] + 1)*jmin + imin)]; + double y_correction = COORD[6*((dims_[0] + 1)*jmin + imin) + 1]; + new_COORD_.resize(num_new_coord, 1e100); + for (int j = jmin; j < jmax + 1; ++j) { + for (int i = imin; i < imax + 1; ++i) { + int pos = (dims_[0] + 1)*j + i; + int new_pos = (new_dims_[0] + 1)*(j-jmin) + (i-imin); + // Copy all 6 coordinates for a pillar. + std::copy(COORD.begin() + 6*pos, COORD.begin() + 6*(pos + 1), new_COORD_.begin() + 6*new_pos); + if (resettoorigin) { + // Substract lowest x value from all X-coords, similarly for y, and truncate in z-direction + new_COORD_[6*new_pos] -= x_correction; + new_COORD_[6*new_pos + 1] -= y_correction; + new_COORD_[6*new_pos + 2] = 0; + new_COORD_[6*new_pos + 3] -= x_correction; + new_COORD_[6*new_pos + 4] -= y_correction; + new_COORD_[6*new_pos + 5] = zmax-zmin; + } + } + } + + // Get the z limits, check if they must be changed to make a shoe-box. + // This means that zmin must be greater than or equal to the highest + // coordinate of the bottom surface, while zmax must be less than or + // equal to the lowest coordinate of the top surface. + int layersz = 8*dims_[0]*dims_[1]; + const std::vector& ZCORN = parser_.getFloatingPointValue("ZCORN"); + int num_zcorn = ZCORN.size(); + if (num_zcorn != layersz*dims_[2]) { + std::cerr << "Error! ZCORN size (" << ZCORN.size() << ") not consistent with SPECGRID\n"; + throw std::runtime_error("Inconsistent ZCORN and SPECGRID."); + } + + zmin = std::max(zmin, botmax_); + zmax = std::min(zmax, topmin_); + if (zmin >= zmax) { + std::cerr << "Error: zmin >= zmax (zmin = " << zmin << ", zmax = " << zmax << ")\n"; + throw std::runtime_error("zmin >= zmax"); + } + std::cout << "Chopping subsample, i: (" << imin << "--" << imax << ") j: (" << jmin << "--" << jmax << ") z: (" << zmin << "--" << zmax << ")" << std::endl; + + // We must find the maximum and minimum k value for the given z limits. + // First, find the first layer with a z-coordinate strictly above zmin. + int kmin = -1; + for (int k = 0; k < dims_[2]; ++k) { + double layer_max = *std::max_element(ZCORN.begin() + k*layersz, ZCORN.begin() + (k + 1)*layersz); + if (layer_max > zmin) { + kmin = k; + break; + } + } + // Then, find the last layer with a z-coordinate strictly below zmax. + int kmax = -1; + for (int k = dims_[2]; k > 0; --k) { + double layer_min = *std::min_element(ZCORN.begin() + (k - 1)*layersz, ZCORN.begin() + k*layersz); + if (layer_min < zmax) { + kmax = k; + break; + } + } + new_dims_[2] = kmax - kmin; + + // Filter the ZCORN field, build mapping from new to old cells. + double z_origin_correction = 0.0; + if (resettoorigin) { + z_origin_correction = zmin; + } + new_ZCORN_.resize(8*new_dims_[0]*new_dims_[1]*new_dims_[2], 1e100); + new_to_old_cell_.resize(new_dims_[0]*new_dims_[1]*new_dims_[2], -1); + int cellcount = 0; + int delta[3] = { 1, 2*dims_[0], 4*dims_[0]*dims_[1] }; + int new_delta[3] = { 1, 2*new_dims_[0], 4*new_dims_[0]*new_dims_[1] }; + for (int k = kmin; k < kmax; ++k) { + for (int j = jmin; j < jmax; ++j) { + for (int i = imin; i < imax; ++i) { + new_to_old_cell_[cellcount++] = dims_[0]*dims_[1]*k + dims_[0]*j + i; + int old_ix = 2*(i*delta[0] + j*delta[1] + k*delta[2]); + int new_ix = 2*((i-imin)*new_delta[0] + (j-jmin)*new_delta[1] + (k-kmin)*new_delta[2]); + int old_indices[8] = { old_ix, old_ix + delta[0], + old_ix + delta[1], old_ix + delta[1] + delta[0], + old_ix + delta[2], old_ix + delta[2] + delta[0], + old_ix + delta[2] + delta[1], old_ix + delta[2] + delta[1] + delta[0] }; + int new_indices[8] = { new_ix, new_ix + new_delta[0], + new_ix + new_delta[1], new_ix + new_delta[1] + new_delta[0], + new_ix + new_delta[2], new_ix + new_delta[2] + new_delta[0], + new_ix + new_delta[2] + new_delta[1], new_ix + new_delta[2] + new_delta[1] + new_delta[0] }; + for (int cc = 0; cc < 8; ++cc) { + new_ZCORN_[new_indices[cc]] = std::min(zmax, std::max(zmin, ZCORN[old_indices[cc]])) - z_origin_correction; + } + } + } + } + + filterIntegerField("ACTNUM", new_ACTNUM_); + filterDoubleField("PORO", new_PORO_); + filterDoubleField("PERMX", new_PERMX_); + filterDoubleField("PERMY", new_PERMY_); + filterDoubleField("PERMZ", new_PERMZ_); + filterIntegerField("SATNUM", new_SATNUM_); + } + + + + /// Return a subparser with fields corresponding to the selected subset. + /// Note that the returned parser is NOT converted to SI, that must be done + /// by the user afterwards with the parser's convertToSI() method. + EclipseGridParser subparser() + { + if (parser_.hasField("FIELD") || parser_.hasField("LAB") || parser_.hasField("PVT-M")) { + THROW("CornerPointChopper::subparser() cannot handle any eclipse unit system other than METRIC."); + } + + EclipseGridParser sp; + std::tr1::shared_ptr sg(new SPECGRID); + for (int dd = 0; dd < 3; ++dd) { + sg->dimensions[dd] = new_dims_[dd]; + } + sp.setSpecialField("SPECGRID", sg); + sp.setFloatingPointField("COORD", new_COORD_); + sp.setFloatingPointField("ZCORN", new_ZCORN_); + if (!new_ACTNUM_.empty()) sp.setIntegerField("ACTNUM", new_ACTNUM_); + if (!new_PORO_.empty()) sp.setFloatingPointField("PORO", new_PORO_); + if (!new_PERMX_.empty()) sp.setFloatingPointField("PERMX", new_PERMX_); + if (!new_PERMY_.empty()) sp.setFloatingPointField("PERMY", new_PERMY_); + if (!new_PERMZ_.empty()) sp.setFloatingPointField("PERMZ", new_PERMZ_); + if (!new_SATNUM_.empty()) sp.setIntegerField("SATNUM", new_SATNUM_); + sp.computeUnits(); // Always METRIC, since that is default. + return sp; + } + + + + + void writeGrdecl(const std::string& filename) + { + // Output new versions of SPECGRID, COORD, ZCORN, ACTNUM, PERMX, PORO, SATNUM. + std::ofstream out(filename.c_str()); + if (!out) { + std::cerr << "Could not open file " << filename << "\n"; + throw std::runtime_error("Could not open output file."); + } + out << "SPECGRID\n" << new_dims_[0] << ' ' << new_dims_[1] << ' ' << new_dims_[2] + << " 1 F\n/\n\n"; + out << "COORD\n"; + int num_new_coord = new_COORD_.size(); + for (int i = 0; i < num_new_coord/6; ++i) { + for (int j = 0; j < 6; ++j) { + out << " " << new_COORD_[6*i + j]; + } + out << '\n'; + } + out << "/\n\n"; + out << "ZCORN\n"; + int num_new_zcorn = new_ZCORN_.size(); + assert(num_new_zcorn%8 == 0); + for (int i = 0; i < num_new_zcorn/8; ++i) { + for (int j = 0; j < 8; ++j) { + out << " " << new_ZCORN_[8*i + j]; + } + out << '\n'; + } + out << "/\n\n"; + + outputField(out, new_ACTNUM_, "ACTNUM"); + outputField(out, new_PORO_, "PORO"); + outputField(out, new_PERMX_, "PERMX"); + outputField(out, new_PERMY_, "PERMY"); + outputField(out, new_PERMZ_, "PERMZ"); + outputField(out, new_SATNUM_, "SATNUM"); + } + + private: + EclipseGridParser parser_; + double botmax_; + double topmin_; + double abszmin_; + double abszmax_; + std::vector new_COORD_; + std::vector new_ZCORN_; + std::vector new_ACTNUM_; + std::vector new_PORO_; + std::vector new_PERMX_; + std::vector new_PERMY_; + std::vector new_PERMZ_; + std::vector new_SATNUM_; + int dims_[3]; + int new_dims_[3]; + std::vector new_to_old_cell_; + + + template + void outputField(std::ostream& os, + const std::vector& field, + const std::string& keyword) + { + if (field.empty()) return; + + os << keyword << '\n'; + int sz = field.size(); + //int num_new_zcorn = new_ZCORN_.size(); + //assert(sz%20 == 0); + const int nel_per_row = 20; + int num_full_rows=sz/nel_per_row; + int num_extra_entries=sz%nel_per_row; + for (int i = 0; i < num_full_rows; ++i) { + for (int j = 0; j < nel_per_row; ++j) { + os << " " << field[nel_per_row*i + j]; + } + os << '\n'; + } + for (int i = 0; i < num_extra_entries; ++i) { + os << " " << field[num_full_rows*nel_per_row+i]; + } + os << "/\n\n"; + } + +// os << keyword << '\n'; +// int sz = field.size(); +// T last = std::numeric_limits::max(); +// int repeats = 0; +// for (int i = 0; i < sz; ++i) { +// T val = field[i]; +// if (val == last) { +// ++repeats; +// } else { +// if (repeats == 1) { +// os << last << '\n'; +// } else if (repeats > 1) { +// os << repeats << '*' << last << '\n'; +// } +// last = val; +// repeats = 1; +// } +// } +// if (repeats == 1) { +// os << last << '\n'; +// } else if (repeats > 1) { +// os << repeats << '*' << last << '\n'; +// } +// os << "/\n\n"; +// } + + + template + void filterField(const std::vector& field, + std::vector& output_field) + { + int sz = new_to_old_cell_.size(); + output_field.resize(sz); + for (int i = 0; i < sz; ++i) { + output_field[i] = field[new_to_old_cell_[i]]; + } + } + + void filterDoubleField(const std::string& keyword, std::vector& output_field) + { + if (parser_.hasField(keyword)) { + const std::vector& field = parser_.getFloatingPointValue(keyword); + filterField(field, output_field); + } + } + + void filterIntegerField(const std::string& keyword, std::vector& output_field) + { + if (parser_.hasField(keyword)) { + const std::vector& field = parser_.getIntegerValue(keyword); + filterField(field, output_field); + } + } + + }; + +} + + + + +#endif // OPM_CORNERPOINTCHOPPER_HEADER_INCLUDED diff --git a/opm/core/eclipse/EclipseGridInspector.cpp b/opm/core/eclipse/EclipseGridInspector.cpp new file mode 100644 index 00000000..b62e4198 --- /dev/null +++ b/opm/core/eclipse/EclipseGridInspector.cpp @@ -0,0 +1,335 @@ +//=========================================================================== +// +// File: EclipseGridInspector.C +// +// Created: Mon Jun 2 12:17:51 2008 +// +// Author: Atgeirr F Rasmussen +// +// $Date$ +// +// $Revision$ +// +// Revision: $Id: EclipseGridInspector.C,v 1.2 2008/08/18 14:16:13 atgeirr Exp $ +// +//=========================================================================== + +/* + Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. + Copyright 2009, 2010 Statoil ASA. + + This file is part of The Open Reservoir Simulator Project (OpenRS). + + OpenRS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenRS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenRS. If not, see . +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Dune +{ + +EclipseGridInspector::EclipseGridInspector(const EclipseGridParser& parser) + : parser_(parser) +{ + std::vector keywords; + keywords.push_back("COORD"); + keywords.push_back("ZCORN"); + + if (!parser_.hasFields(keywords)) { + THROW("Needed field is missing in file"); + } + + if (parser_.hasField("SPECGRID")) { + const SPECGRID& sgr = parser.getSPECGRID(); + logical_gridsize_[0] = sgr.dimensions[0]; + logical_gridsize_[1] = sgr.dimensions[1]; + logical_gridsize_[2] = sgr.dimensions[2]; + } else if (parser_.hasField("DIMENS")) { + const std::vector& dim = parser.getIntegerValue("DIMENS"); + logical_gridsize_[0] = dim[0]; + logical_gridsize_[1] = dim[1]; + logical_gridsize_[2] = dim[2]; + } else { + THROW("Found neither SPECGRID nor DIMENS in file. At least one is needed."); + } + +} + +/** + Return the dip slopes for the cell relative to xy-plane in x- and y- direction. + Dip slope is average rise in positive x-direction over cell length in x-direction. + Similarly for y. + + Current implementation is for vertical pillars, but is not difficult to fix. + + @returns a std::pair with x-dip in first component and y-dip in second. +*/ +std::pair EclipseGridInspector::cellDips(int i, int j, int k) const +{ + checkLogicalCoords(i, j, k); + const std::vector& pillc = parser_.getFloatingPointValue("COORD"); + int num_pillars = (logical_gridsize_[0] + 1)*(logical_gridsize_[1] + 1); + if (6*num_pillars != int(pillc.size())) { + throw std::runtime_error("Wrong size of COORD field."); + } + const std::vector& z = parser_.getFloatingPointValue("ZCORN"); + int num_cells = logical_gridsize_[0]*logical_gridsize_[1]*logical_gridsize_[2]; + if (8*num_cells != int(z.size())) { + throw std::runtime_error("Wrong size of ZCORN field"); + } + + // Pick ZCORN-value for all 8 corners of the given cell + boost::array cellz = cellZvals(i, j, k); + + // Compute rise in positive x-direction for all four edges (and then find mean) + // Current implementation is for regularly placed and vertical pillars! + int numxpill = logical_gridsize_[0] + 1; + int pix = i + j*numxpill; + double cell_xlength = pillc[6*(pix + 1)] - pillc[6*pix]; + flush(std::cout); + double xrise[4] = { (cellz[1] - cellz[0])/cell_xlength, // LLL -> HLL + (cellz[3] - cellz[2])/cell_xlength, // LHL -> HHL + (cellz[5] - cellz[4])/cell_xlength, // LLH -> HLH + (cellz[7] - cellz[6])/cell_xlength}; // LHH -> HHH + + double cell_ylength = pillc[6*(pix + numxpill) + 1] - pillc[6*pix + 1]; + double yrise[4] = { (cellz[2] - cellz[0])/cell_ylength, // LLL -> LHL + (cellz[3] - cellz[1])/cell_ylength, // HLL -> HHL + (cellz[6] - cellz[4])/cell_ylength, // LLH -> LHH + (cellz[7] - cellz[5])/cell_ylength}; // HLH -> HHH + + + // Now ignore those edges that touch the global top or bottom surface + // of the entire grdecl model. This is to avoid bias, as these edges probably + // don't follow an overall dip for the model if it exists. + int x_edges = 4; + int y_edges = 4; + boost::array gridlimits = getGridLimits(); + double zmin = gridlimits[4]; + double zmax = gridlimits[5]; + // LLL -> HLL + if ((cellz[1] == zmin) || (cellz[0] == zmin)) { + xrise[0] = 0; x_edges--; + } + // LHL -> HHL + if ((cellz[2] == zmin) || (cellz[3] == zmin)) { + xrise[1] = 0; x_edges--; + } + // LLH -> HLH + if ((cellz[4] == zmax) || (cellz[5] == zmax)) { + xrise[2] = 0; x_edges--; + } + // LHH -> HHH + if ((cellz[6] == zmax) || (cellz[7] == zmax)) { + xrise[3] = 0; x_edges--; + } + // LLL -> LHL + if ((cellz[0] == zmin) || (cellz[2] == zmin)) { + yrise[0] = 0; y_edges--; + } + // HLL -> HHL + if ((cellz[1] == zmin) || (cellz[3] == zmin)) { + yrise[1] = 0; y_edges--; + } + // LLH -> LHH + if ((cellz[6] == zmax) || (cellz[4] == zmax)) { + yrise[2] = 0; y_edges--; + } + // HLH -> HHH + if ((cellz[7] == zmax) || (cellz[5] == zmax)) { + yrise[3] = 0; y_edges--; + } + + return std::make_pair( (xrise[0] + xrise[1] + xrise[2] + xrise[3])/x_edges, + (yrise[0] + yrise[1] + yrise[2] + yrise[3])/y_edges); +} +/** + Wrapper for cellDips(i, j, k). +*/ +std::pair EclipseGridInspector::cellDips(int cell_idx) const +{ + boost::array idxs = cellIdxToLogicalCoords(cell_idx); + return cellDips(idxs[0], idxs[1], idxs[2]); +} + +boost::array EclipseGridInspector::cellIdxToLogicalCoords(int cell_idx) const +{ + + int i,j,k; // Position of cell in cell hierarchy + int horIdx = (cell_idx+1) - int(std::floor(((double)(cell_idx+1))/((double)(logical_gridsize_[0]*logical_gridsize_[1]))))*logical_gridsize_[0]*logical_gridsize_[1]; // index in the corresponding horizon + if (horIdx == 0) { + horIdx = logical_gridsize_[0]*logical_gridsize_[1]; + } + i = horIdx - int(std::floor(((double)horIdx)/((double)logical_gridsize_[0])))*logical_gridsize_[0]; + if (i == 0) { + i = logical_gridsize_[0]; + } + j = (horIdx-i)/logical_gridsize_[0]+1; + k = ((cell_idx+1)-logical_gridsize_[0]*(j-1)-1)/(logical_gridsize_[0]*logical_gridsize_[1])+1; + + boost::array a = {{i-1, j-1, k-1}}; + return a; //boost::array {{i-1, j-1, k-1}}; +} + +double EclipseGridInspector::cellVolumeVerticalPillars(int i, int j, int k) const +{ + // Checking parameters and obtaining values from parser. + checkLogicalCoords(i, j, k); + const std::vector& pillc = parser_.getFloatingPointValue("COORD"); + int num_pillars = (logical_gridsize_[0] + 1)*(logical_gridsize_[1] + 1); + if (6*num_pillars != int(pillc.size())) { + throw std::runtime_error("Wrong size of COORD field."); + } + const std::vector& z = parser_.getFloatingPointValue("ZCORN"); + int num_cells = logical_gridsize_[0]*logical_gridsize_[1]*logical_gridsize_[2]; + if (8*num_cells != int(z.size())) { + throw std::runtime_error("Wrong size of ZCORN field"); + } + + // Computing the base area as half the 2d cross product of the diagonals. + int numxpill = logical_gridsize_[0] + 1; + int pix = i + j*numxpill; + double px[4] = { pillc[6*pix], + pillc[6*(pix + 1)], + pillc[6*(pix + numxpill)], + pillc[6*(pix + numxpill + 1)] }; + double py[4] = { pillc[6*pix + 1], + pillc[6*(pix + 1) + 1], + pillc[6*(pix + numxpill) + 1], + pillc[6*(pix + numxpill + 1) + 1] }; + double diag1[2] = { px[3] - px[0], py[3] - py[0] }; + double diag2[2] = { px[2] - px[1], py[2] - py[1] }; + double area = 0.5*(diag1[0]*diag2[1] - diag1[1]*diag2[0]); + + // Computing the average of the z-differences along each pillar. + int delta[3] = { 1, + 2*logical_gridsize_[0], + 4*logical_gridsize_[0]*logical_gridsize_[1] }; + int ix = 2*(i*delta[0] + j*delta[1] + k*delta[2]); + double cellz[8] = { z[ix], z[ix + delta[0]], + z[ix + delta[1]], z[ix + delta[1] + delta[0]], + z[ix + delta[2]], z[ix + delta[2] + delta[0]], + z[ix + delta[2] + delta[1]], z[ix + delta[2] + delta[1] + delta[0]] }; + double diffz[4] = { cellz[4] - cellz[0], + cellz[5] - cellz[1], + cellz[6] - cellz[2], + cellz[7] - cellz[3] }; + double averzdiff = 0.25*std::accumulate(diffz, diffz + 4, 0.0); + return averzdiff*area; +} + + +double EclipseGridInspector::cellVolumeVerticalPillars(int cell_idx) const +{ + boost::array idxs = cellIdxToLogicalCoords(cell_idx); + return cellVolumeVerticalPillars(idxs[0], idxs[1], idxs[2]); +} + +void EclipseGridInspector::checkLogicalCoords(int i, int j, int k) const +{ + if (i < 0 || i >= logical_gridsize_[0]) + throw std::runtime_error("First coordinate out of bounds"); + if (j < 0 || j >= logical_gridsize_[1]) + throw std::runtime_error("Second coordinate out of bounds"); + if (k < 0 || k >= logical_gridsize_[2]) + throw std::runtime_error("Third coordinate out of bounds"); +} + + +boost::array EclipseGridInspector::getGridLimits() const +{ + if (! (parser_.hasField("COORD") && parser_.hasField("ZCORN") && parser_.hasField("SPECGRID")) ) { + throw std::runtime_error("EclipseGridInspector: Grid does not have SPECGRID, COORD, and ZCORN, can't find dimensions."); + } + + std::vector coord = parser_.getFloatingPointValue("COORD"); + std::vector zcorn = parser_.getFloatingPointValue("ZCORN"); + + double xmin = +DBL_MAX; + double xmax = -DBL_MAX; + double ymin = +DBL_MAX; + double ymax = -DBL_MAX; + + + int pillars = (logical_gridsize_[0]+1) * (logical_gridsize_[1]+1); + + for (int pillarindex = 0; pillarindex < pillars; ++pillarindex) { + if (coord[pillarindex * 6 + 0] > xmax) + xmax = coord[pillarindex * 6 + 0]; + if (coord[pillarindex * 6 + 0] < xmin) + xmin = coord[pillarindex * 6 + 0]; + if (coord[pillarindex * 6 + 1] > ymax) + ymax = coord[pillarindex * 6 + 1]; + if (coord[pillarindex * 6 + 1] < ymin) + ymin = coord[pillarindex * 6 + 1]; + if (coord[pillarindex * 6 + 3] > xmax) + xmax = coord[pillarindex * 6 + 3]; + if (coord[pillarindex * 6 + 3] < xmin) + xmin = coord[pillarindex * 6 + 3]; + if (coord[pillarindex * 6 + 4] > ymax) + ymax = coord[pillarindex * 6 + 4]; + if (coord[pillarindex * 6 + 4] < ymin) + ymin = coord[pillarindex * 6 + 4]; + } + + boost::array gridlimits = {{ xmin, xmax, ymin, ymax, + *min_element(zcorn.begin(), zcorn.end()), + *max_element(zcorn.begin(), zcorn.end()) }}; + return gridlimits; +} + + + +boost::array EclipseGridInspector::gridSize() const +{ + boost::array retval = {{ logical_gridsize_[0], + logical_gridsize_[1], + logical_gridsize_[2] }}; + return retval; +} + + +boost::array EclipseGridInspector::cellZvals(int i, int j, int k) const +{ + // Get the zcorn field. + const std::vector& z = parser_.getFloatingPointValue("ZCORN"); + int num_cells = logical_gridsize_[0]*logical_gridsize_[1]*logical_gridsize_[2]; + if (8*num_cells != int(z.size())) { + throw std::runtime_error("Wrong size of ZCORN field"); + } + + // Make the coordinate array. + int delta[3] = { 1, + 2*logical_gridsize_[0], + 4*logical_gridsize_[0]*logical_gridsize_[1] }; + int ix = 2*(i*delta[0] + j*delta[1] + k*delta[2]); + boost::array cellz = {{ z[ix], z[ix + delta[0]], + z[ix + delta[1]], z[ix + delta[1] + delta[0]], + z[ix + delta[2]], z[ix + delta[2] + delta[0]], + z[ix + delta[2] + delta[1]], z[ix + delta[2] + delta[1] + delta[0]] }}; + return cellz; +} + + +} // namespace Dune diff --git a/opm/core/eclipse/EclipseGridInspector.hpp b/opm/core/eclipse/EclipseGridInspector.hpp new file mode 100644 index 00000000..c314d28e --- /dev/null +++ b/opm/core/eclipse/EclipseGridInspector.hpp @@ -0,0 +1,105 @@ +//=========================================================================== +// +// File: EclipseGridInspector.h +// +// Created: Mon Jun 2 09:46:08 2008 +// +// Author: Atgeirr F Rasmussen +// +// $Date$ +// +// Revision: $Id: EclipseGridInspector.h,v 1.2 2008/08/18 14:16:12 atgeirr Exp $ +// +//=========================================================================== + +/* +Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. +Copyright 2009, 2010 Statoil ASA. + +This file is part of The Open Reservoir Simulator Project (OpenRS). + +OpenRS is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenRS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OpenRS. If not, see . +*/ + +#ifndef SINTEF_ECLIPSEGRIDINSPECTOR_HEADER +#define SINTEF_ECLIPSEGRIDINSPECTOR_HEADER + +#include +#include + + +namespace Dune +{ + +/** + @brief A class for inspecting the contents of an eclipse file. + + Given an EclipseGridParser that has successfully read en Eclipse + .grdecl-file, this class may be used to answer certain queries about + its contents. + + @author Atgeirr F. Rasmussen + @date 2008/06/02 09:46:08 +*/ + +class EclipseGridParser; + +class EclipseGridInspector +{ +public: + /// Constructor taking a parser as argument. + /// The parser must already have read an Eclipse file. + EclipseGridInspector(const EclipseGridParser& parser); + + /// Assuming that the pillars are vertical, compute the + /// volume of the cell given by logical coordinates (i, j, k). + double cellVolumeVerticalPillars(int i, int j, int k) const; + + /// Assuming that the pillars are vertical, compute the + /// volume of the cell given by the cell index + double cellVolumeVerticalPillars(int cell_idx) const; + + /// Compute the average dip in x- and y-direction of the + /// cell tops and bottoms relative to the xy-plane + std::pair cellDips(int i, int j, int k) const; + std::pair cellDips(int cell_idx) const; + + // Convert global cell index to logical ijk-coordinates + boost::array cellIdxToLogicalCoords(int cell_idx) const; + + /// Returns a vector with the outer limits of grid (in the grid's unit). + /// The vector contains [xmin, xmax, ymin, ymax, zmin, zmax], as + /// read from COORDS and ZCORN + boost::array getGridLimits() const; + + /// Returns the extent of the logical cartesian grid + /// as number of cells in the (i, j, k) directions. + boost::array gridSize() const; + + /// Returns the eight z-values associated with a given cell. + /// The ordering is such that i runs fastest. That is, with + /// L = low and H = high: + /// {LLL, HLL, LHL, HHL, LLH, HLH, LHH, HHH }. + boost::array cellZvals(int i, int j, int k) const; + +private: + const EclipseGridParser& parser_; + int logical_gridsize_[3]; + void checkLogicalCoords(int i, int j, int k) const; +}; + +} // namespace Dune + +#endif // SINTEF_ECLIPSEGRIDINSPECTOR_HEADER + diff --git a/opm/core/eclipse/EclipseGridParser.cpp b/opm/core/eclipse/EclipseGridParser.cpp new file mode 100644 index 00000000..199c140c --- /dev/null +++ b/opm/core/eclipse/EclipseGridParser.cpp @@ -0,0 +1,571 @@ +//=========================================================================== +// +// File: EclipseGridParser.C +// +// Created: Thu Dec 6 08:46:05 2007 +// +// Author: Atgeirr F Rasmussen +// +// $Date$ +// +// $Revision$ +// +// Revision: $Id: EclipseGridParser.C,v 1.4 2008/08/18 14:16:14 atgeirr Exp $ +// +//=========================================================================== + +/* + Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. + Copyright 2009, 2010 Statoil ASA. + + This file is part of The Open Reservoir Simulator Project (OpenRS). + + OpenRS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenRS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenRS. If not, see . +*/ +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include +#include "EclipseGridParser.hpp" +#include "EclipseGridParserHelpers.hpp" +#include "SpecialEclipseFields.hpp" +#include +#include +#include + +using namespace std; + +//#define VERBOSE + +namespace Dune +{ + +// ---------- List of supported keywords ---------- + +namespace EclipseKeywords +{ + string integer_fields[] = + { string("ACTNUM"), + string("SATNUM"), + string("EQLNUM"), + string("REGNUM"), + string("ROCKTYPE"), + string("DIMENS"), + string("REGDIMS"), + string("WELLDIMS"), + string("TABDIMS"), + string("FIPNUM"), + string("GRIDFILE") + }; + const int num_integer_fields = sizeof(integer_fields) / sizeof(integer_fields[0]); + + string floating_fields[] = + { string("COORD"), string("ZCORN"), string("PERMX"), + string("PERMY"), string("PERMZ"), string("PERMXX"), + string("PERMYY"), string("PERMZZ"), string("PERMXY"), + string("PERMYZ"), string("PERMZX"), string("PORO"), + string("BULKMOD"), string("YOUNGMOD"), string("LAMEMOD"), + string("SHEARMOD"), string("POISSONMOD"), string("PWAVEMOD"), + string("MULTPV"), string("PRESSURE"), string("SGAS"), + string("SWAT") + }; + const int num_floating_fields = sizeof(floating_fields) / sizeof(floating_fields[0]); + + string special_fields[] = + { string("SPECGRID"), string("FAULTS"), string("MULTFLT"), + string("TITLE"), string("START"), string("DATES"), + string("DENSITY"), string("PVDG"), string("PVDO"), + string("PVTG"), string("PVTO"), string("PVTW"), + string("SGOF"), string("SWOF"), string("ROCK"), + string("ROCKTAB"), string("WELSPECS"), string("COMPDAT"), + string("WCONINJE"), string("WCONPROD"), string("WELTARG"), + string("EQUIL"), string("PVCDO"), + // The following fields only have a dummy implementation + // that allows us to ignore them. + "SWFN", + "SOF2", + "TUNING" + }; + + const int num_special_fields = sizeof(special_fields) / sizeof(special_fields[0]); + + string ignore_with_data[] = + { string("MAPUNITS"), string("MAPAXES"), string("GRIDUNIT"), + string("NTG"), string("REGDIMS"), string("WELLDIMS"), + string("NSTACK"), string("SATNUM"), + string("RPTRST"), string("ROIP"), string("RWIP"), + string("RWSAT"), string("RPR"), string("WBHP"), + string("WOIR"), string("TSTEP"), string("BOX"), + string("COORDSYS"), string("PBVD") + }; + const int num_ignore_with_data = sizeof(ignore_with_data) / sizeof(ignore_with_data[0]); + + string ignore_no_data[] = + { string("RUNSPEC"), string("WATER"), string("OIL"), + string("METRIC"), string("FMTIN"), string("FMTOUT"), + string("GRID"), string("INIT"), string("NOECHO"), + string("ECHO"), string("EDIT"), string("PROPS"), + string("REGIONS"), string("SOLUTION"), string("SUMMARY"), + string("FPR"), string("FOIP"), string("FWIP"), + string("RUNSUM"), string("EXCEL"), string("SCHEDULE"), + string("END"), string("ENDBOX"), string("CONTINUE"), + string("NONNC"), string("GAS"), string("DISGAS"), + string("FIELD") + }; + const int num_ignore_no_data = sizeof(ignore_no_data) / sizeof(ignore_no_data[0]); + + string include_keywords[] = { string("INCLUDE") }; + const int num_include_keywords = sizeof(include_keywords) / sizeof(include_keywords[0]); + + +} // namespace EclipseKeywords + +namespace { + + enum FieldType { + Integer, + FloatingPoint, + SpecialField, + IgnoreWithData, + IgnoreNoData, + Include, + Unknown + }; + + inline FieldType classifyKeyword(const string& keyword) + { + using namespace EclipseKeywords; + if (count(integer_fields, integer_fields + num_integer_fields, keyword)) { + return Integer; + } else if (count(floating_fields, floating_fields + num_floating_fields, keyword)) { + return FloatingPoint; + } else if (count(special_fields, special_fields + num_special_fields, keyword)) { + return SpecialField; + } else if (count(ignore_with_data, ignore_with_data + num_ignore_with_data, keyword)) { + return IgnoreWithData; + } else if (count(ignore_no_data, ignore_no_data + num_ignore_no_data, keyword)) { + return IgnoreNoData; + } else if (count(include_keywords, include_keywords + num_include_keywords, keyword)) { + return Include; + } else { + return Unknown; + } + } + +} // anon namespace + + + +// ---------- Member functions ---------- + +/// Default constructor. +//--------------------------------------------------------------------------- +EclipseGridParser::EclipseGridParser() +//--------------------------------------------------------------------------- +{ +} + + +/// Constructor taking an eclipse filename. +//--------------------------------------------------------------------------- +EclipseGridParser::EclipseGridParser(const string& filename, bool convert_to_SI) +//--------------------------------------------------------------------------- +{ + // Store directory of filename + boost::filesystem::path p(filename); + directory_ = p.parent_path().string(); + ifstream is(filename.c_str()); + if (!is) { + cerr << "Unable to open file " << filename << endl; + throw exception(); + } + read(is, convert_to_SI); +} + + +/// Read the given stream, overwriting any previous data. +//--------------------------------------------------------------------------- +void EclipseGridParser::read(istream& is, bool convert_to_SI) +//--------------------------------------------------------------------------- +{ + integer_field_map_.clear(); + floating_field_map_.clear(); + special_field_map_.clear(); + + readImpl(is); + computeUnits(); + if (convert_to_SI) { + convertToSI(); + } +} + +//--------------------------------------------------------------------------- +void EclipseGridParser::readImpl(istream& is) +//--------------------------------------------------------------------------- +{ + if (!is) { + cerr << "Could not read given input stream." << endl; + throw exception(); + } + + // Make temporary maps that will at the end be swapped with the + // member maps + // NOTE: Above is no longer true, for easier implementation of + // the INCLUDE keyword. We lose the strong exception guarantee, + // though (of course retaining the basic guarantee). + map >& intmap = integer_field_map_; + map >& floatmap = floating_field_map_; + map >& specialmap = special_field_map_; + + // Actually read the data + is >> ignoreWhitespace; + while (!is.eof()) { + string keyword = readKeyword(is); +#ifdef VERBOSE + cout << "Keyword found: " << keyword << endl; +#endif + FieldType type = classifyKeyword(keyword); + switch (type) { + case Integer: + readVectorData(is, intmap[keyword]); + break; + case FloatingPoint: + readVectorData(is, floatmap[keyword]); + break; + case SpecialField: { + std::tr1::shared_ptr sb_ptr = createSpecialField(is, keyword); + if (sb_ptr) { + specialmap[keyword] = sb_ptr; + } else { + THROW("Could not create field " << keyword); + } + break; + } + case IgnoreWithData: { + ignored_fields_.insert(keyword); + is >> ignoreSlashLine; +#ifdef VERBOSE + cout << "(ignored)" << endl; +#endif + break; + } + case IgnoreNoData: { + ignored_fields_.insert(keyword); + is >> ignoreLine; +#ifdef VERBOSE + cout << "(ignored)" << endl; +#endif + break; + } + case Include: { + string include_filename = readString(is); + if (!directory_.empty()) { + include_filename = directory_ + '/' + include_filename; + } + ifstream include_is(include_filename.c_str()); + if (!include_is) { + THROW("Unable to open INCLUDEd file " << include_filename); + } + readImpl(include_is); + is >> ignoreSlashLine; + break; + } + case Unknown: + default: + cerr << "Keyword " << keyword << " not recognized." << endl; + throw exception(); + } + is >> ignoreWhitespace; + } + +#define VERBOSE_LIST_FIELDS 0 +#if VERBOSE_LIST_FIELDS + std::cout << "\nInteger fields:\n"; + for (std::map >::iterator + i = intmap.begin(); i != intmap.end(); ++i) + std::cout << '\t' << i->first << '\n'; + + std::cout << "\nFloat fields:\n"; + for (std::map >::iterator + i = floatmap.begin(); i != floatmap.end(); ++i) + std::cout << '\t' << i->first << '\n'; + + std::cout << "\nSpecial fields:\n"; + for (std::map >::iterator + i = specialmap.begin(); i != specialmap.end(); ++i) + std::cout << '\t' << i->first << '\n'; +#endif +} + + + +//--------------------------------------------------------------------------- +void EclipseGridParser::convertToSI() +//--------------------------------------------------------------------------- +{ + // Convert all special fields. + typedef std::map >::iterator SpecialIt; + for (SpecialIt i = special_field_map_.begin(); i != special_field_map_.end(); ++i) { + i->second->convertToSI(units_); + } + + // Convert all floating point fields. + typedef std::map >::iterator FloatIt; + for (FloatIt i = floating_field_map_.begin(); i != floating_field_map_.end(); ++i) { + const std::string& key = i->first; + std::vector& field = i->second; + // Find the right unit. + double unit = 1e100; + if (key == "COORD" || key == "ZCORN") { + unit = units_.length; + } else if (key == "PERMX" || key == "PERMY" || key == "PERMZ" || + key == "PERMXX" || key == "PERMYY" || key == "PERMZZ" || + key == "PERMXY" || key == "PERMYZ" || key == "PERMZX") { + unit = units_.permeability; + } else if (key == "PORO" || key == "BULKMOD" || key == "YOUNGMOD" || + key == "LAMEMOD" || key == "SHEARMOD" || key == "POISSONMOD" || + key == "PWAVEMOD" || key == "MULTPV" || key == "PWAVEMOD" || + key == "SGAS" || key == "SWAT") { + unit = 1.0; + } else if (key == "PRESSURE") { + unit = units_.pressure; + } else { + THROW("Units for field " << key << " not specified. Cannon convert to SI."); + } + // Convert. + for (int i = 0; i < int(field.size()); ++i) { + field[i] = Dune::unit::convert::from(field[i], unit); + } + } + + // Set all units to one. + units_.setToOne(); +} + + +/// Returns true if the given keyword corresponds to a field that +/// was found in the file. +//--------------------------------------------------------------------------- +bool EclipseGridParser::hasField(const string& keyword) const +//--------------------------------------------------------------------------- +{ + string ukey = upcase(keyword); + return integer_field_map_.count(ukey) || floating_field_map_.count(ukey) || + special_field_map_.count(ukey) || ignored_fields_.count(ukey); +} + + +/// Returns true if all the given keywords correspond to fields +/// that were found in the file. +//--------------------------------------------------------------------------- +bool EclipseGridParser::hasFields(const vector& keywords) const +//--------------------------------------------------------------------------- +{ + int num_keywords = keywords.size(); + for (int i = 0; i < num_keywords; ++i) { + if (!hasField(keywords[i])) { + return false; + } + } + return true; +} + +//--------------------------------------------------------------------------- +vector EclipseGridParser::fieldNames() const +//--------------------------------------------------------------------------- +{ + vector names; + names.reserve(integer_field_map_.size() + + floating_field_map_.size() + + special_field_map_.size() + + ignored_fields_.size()); + { + map >::const_iterator it = integer_field_map_.begin(); + for (; it != integer_field_map_.end(); ++it) { + names.push_back(it->first); + } + } + { + map >::const_iterator it = floating_field_map_.begin(); + for (; it != floating_field_map_.end(); ++it) { + names.push_back(it->first); + } + } + { + map >::const_iterator it = special_field_map_.begin(); + for (; it != special_field_map_.end(); ++it) { + names.push_back(it->first); + } + } + { + set::const_iterator it = ignored_fields_.begin(); + for (; it != ignored_fields_.end(); ++it) { + names.push_back(*it); + } + } + return names; +} + +//--------------------------------------------------------------------------- +const std::vector& EclipseGridParser::getIntegerValue(const std::string& keyword) const +//--------------------------------------------------------------------------- +{ + if (keyword == "SPECGRID") { + cerr << "\nERROR. Interface has changed!\n" + << "const vector& dim = parser.getIntegerValue(""SPECGRID"") is deprecated.\n" + << "Use:\n" + << "const SPECGRID& specgrid = parser.getSPECGRID();\n" + << "const vector& dim = specgrid.dimensions;\n\n"; + throw exception(); + } + + map >::const_iterator it + = integer_field_map_.find(keyword); + if (it == integer_field_map_.end()) { + return empty_integer_field_; + } else { + return it->second; + } +} + +//--------------------------------------------------------------------------- +const std::vector& EclipseGridParser::getFloatingPointValue(const std::string& keyword) const +//--------------------------------------------------------------------------- +{ + map >::const_iterator it + = floating_field_map_.find(keyword); + if (it == floating_field_map_.end()) { + return empty_floating_field_; + } else { + return it->second; + } +} + + +//--------------------------------------------------------------------------- +const std::tr1::shared_ptr EclipseGridParser::getSpecialValue(const std::string& keyword) const +//--------------------------------------------------------------------------- +{ + map >::const_iterator it = special_field_map_.find(keyword); + if (it == special_field_map_.end()) { + THROW("No such field: " << keyword); + } else { + return it->second; + } +} + +//--------------------------------------------------------------------------- +std::tr1::shared_ptr +EclipseGridParser::createSpecialField(std::istream& is, + const std::string& fieldname) +//--------------------------------------------------------------------------- +{ + string ukey = upcase(fieldname); + std::tr1::shared_ptr spec_ptr + = Factory::createObject(fieldname); + spec_ptr->read(is); + return spec_ptr; +} + +//--------------------------------------------------------------------------- +void EclipseGridParser::setIntegerField(const std::string& keyword, + const std::vector& field) +//--------------------------------------------------------------------------- +{ + integer_field_map_[keyword] = field; +} + +//--------------------------------------------------------------------------- +void EclipseGridParser::setFloatingPointField(const std::string& keyword, + const std::vector& field) +//--------------------------------------------------------------------------- +{ + floating_field_map_[keyword] = field; +} + +//--------------------------------------------------------------------------- +void EclipseGridParser::setSpecialField(const std::string& keyword, + std::tr1::shared_ptr field) +//--------------------------------------------------------------------------- +{ + special_field_map_[keyword] = field; +} + +//--------------------------------------------------------------------------- +const EclipseUnits& EclipseGridParser::units() const +//--------------------------------------------------------------------------- +{ + return units_; +} + +//--------------------------------------------------------------------------- +void EclipseGridParser::computeUnits() +//--------------------------------------------------------------------------- +{ + // Decide unit family. + enum EclipseUnitFamily { Metric = 0, Field = 1, Lab = 2, Pvtm = 3 }; + EclipseUnitFamily unit_family = Metric; // The default. + if (hasField("FIELD")) unit_family = Field; + if (hasField("LAB")) unit_family = Lab; + if (hasField("PVT-M")) unit_family = Pvtm; + + // Set units. + using namespace prefix; + using namespace unit; + switch (unit_family) { + case Metric: + units_.length = meter; + units_.time = day; + units_.density = kilogram/cubic(meter); + units_.pressure = barsa; + units_.compressibility = 1.0/barsa; + units_.viscosity = centi*Poise; + units_.permeability = milli*darcy; + units_.liqvol_s = cubic(meter); + units_.liqvol_r = cubic(meter); + units_.gasvol_s = cubic(meter); + units_.gasvol_r = cubic(meter); + units_.transmissibility = centi*Poise * cubic(meter) / (day * barsa); + break; + case Field: + units_.length = feet; + units_.time = day; + units_.density = pound/cubic(feet); + units_.pressure = psia; + units_.compressibility = 1.0/psia; + units_.viscosity = centi*Poise; + units_.permeability = milli*darcy; + units_.liqvol_s = stb; + units_.liqvol_r = stb; + units_.gasvol_s = 1000*cubic(feet); // Prefix 'M' is 1000 + units_.gasvol_r = stb; + units_.transmissibility = centi*Poise * stb / (day * psia); + break; + case Lab: + THROW("Unhandled unit family " << unit_family); + break; + case Pvtm: + THROW("Unhandled unit family " << unit_family); + break; + default: + THROW("Unknown unit family " << unit_family); + } +} + +} // namespace Dune diff --git a/opm/core/eclipse/EclipseGridParser.hpp b/opm/core/eclipse/EclipseGridParser.hpp new file mode 100644 index 00000000..4a922864 --- /dev/null +++ b/opm/core/eclipse/EclipseGridParser.hpp @@ -0,0 +1,191 @@ +//=========================================================================== +// +// File: EclipseGridParser.h +// +// Created: Wed Dec 5 17:05:13 2007 +// +// Author: Atgeirr F Rasmussen +// +// $Date$ +// +// Revision: $Id: EclipseGridParser.h,v 1.3 2008/08/18 14:16:13 atgeirr Exp $ +// +//=========================================================================== + +/* +Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. +Copyright 2009, 2010 Statoil ASA. + +This file is part of The Open Reservoir Simulator Project (OpenRS). + +OpenRS is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenRS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OpenRS. If not, see . +*/ + +#ifndef SINTEF_ECLIPSEGRIDPARSER_HEADER +#define SINTEF_ECLIPSEGRIDPARSER_HEADER + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Dune +{ + +/** + @brief A class for reading and parsing all fields of an eclipse file. + + This object is constructed using an Eclipse .grdecl-file. All data + fields are extracted upon construction and written to vector data + structures, which can then be read out afterwards via + convenience functions. + + There is also a convenience function to easily check which fields + were successfully parsed. + + @author Atgeirr F. Rasmussen + @date 2007/12/06 13:00:03 + +*/ + +class EclipseGridParser +{ +public: + /// Default constructor. + EclipseGridParser(); + /// Constructor taking an eclipse filename. Unless the second + /// argument 'convert_to_SI' is false, all fields will be + /// converted to SI units. + explicit EclipseGridParser(const std::string& filename, bool convert_to_SI = true); + + /// Read the given stream, overwriting any previous data. Unless + /// the second argument 'convert_to_SI' is false, all fields will + /// be converted to SI units. + void read(std::istream& is, bool convert_to_SI = true); + + /// Convert all data to SI units, according to unit category as + /// specified in the eclipse file (METRIC, FIELD etc.). + /// It is unnecessary to call this if constructed with + /// 'convert_to_SI' equal to true, but it is not an error. + void convertToSI(); + + /// Returns true if the given keyword corresponds to a field that + /// was found in the file. + bool hasField(const std::string& keyword) const; + /// Returns true if all the given keywords correspond to fields + /// that were found in the file. + bool hasFields(const std::vector& keywords) const; + /// The keywords/fields found in the file. + std::vector fieldNames() const; + + /// Returns a reference to a vector containing the values + /// corresponding to the given integer keyword. + const std::vector& getIntegerValue(const std::string& keyword) const; + + /// Returns a reference to a vector containing the values + /// corresponding to the given floating-point keyword. + const std::vector& getFloatingPointValue(const std::string& keyword) const; + + /// Returns a reference to a vector containing pointers to the values + /// corresponding to the given keyword when the values are not only integers + /// or floats. + const std::tr1::shared_ptr getSpecialValue(const std::string& keyword) const; + + // This macro implements support for a special field keyword. It requires that a subclass + // of SpecialBase exists, that has the same name as the keyword. + // After using SPECIAL_FIELD(KEYWORD), the public member getKEYWORD will be available. +#define SPECIAL_FIELD(keyword) \ +private: \ + struct X##keyword { X##keyword() { Factory::addCreator(#keyword); } }; \ + X##keyword x##keyword; \ +public: \ + const keyword& get##keyword() const \ + { return dynamic_cast(*getSpecialValue(#keyword)); } + + // Support for special fields. + SPECIAL_FIELD(SPECGRID); + SPECIAL_FIELD(FAULTS); + SPECIAL_FIELD(MULTFLT); + SPECIAL_FIELD(TITLE); + SPECIAL_FIELD(START); + SPECIAL_FIELD(DATES); + SPECIAL_FIELD(DENSITY); + SPECIAL_FIELD(PVDG); + SPECIAL_FIELD(PVDO); + SPECIAL_FIELD(PVTG); + SPECIAL_FIELD(PVTO); + SPECIAL_FIELD(PVTW); + SPECIAL_FIELD(SGOF); + SPECIAL_FIELD(SWOF); + SPECIAL_FIELD(ROCK); + SPECIAL_FIELD(ROCKTAB); + SPECIAL_FIELD(WELSPECS); + SPECIAL_FIELD(COMPDAT); + SPECIAL_FIELD(WCONINJE); + SPECIAL_FIELD(WCONPROD); + SPECIAL_FIELD(WELTARG); + SPECIAL_FIELD(EQUIL); + SPECIAL_FIELD(PVCDO); + + // The following fields only have a dummy implementation + // that allows us to ignore them. + SPECIAL_FIELD(SWFN); + SPECIAL_FIELD(SOF2); + SPECIAL_FIELD(TUNING); + +#undef SPECIAL_FIELD + + + /// Sets an integer field to have a particular value. + void setIntegerField(const std::string& keyword, const std::vector& field); + + /// Sets a floating point field to have a particular value. + void setFloatingPointField(const std::string& keyword, const std::vector& field); + + /// Sets a special field to have a particular value. + void setSpecialField(const std::string& keyword, std::tr1::shared_ptr field); + + /// Compute the units used by the deck, depending on the presence + /// of keywords such as METRIC, FIELD etc. It is an error to call + /// this after conversion to SI has taken place. + void computeUnits(); + + /// The units specified by the eclipse file read. + const EclipseUnits& units() const; + +private: + std::tr1::shared_ptr createSpecialField(std::istream& is, const std::string& fieldname); + void readImpl(std::istream& is); + + + std::string directory_; + std::map > integer_field_map_; + std::map > floating_field_map_; + std::map > special_field_map_; + std::set ignored_fields_; + std::vector empty_integer_field_; + std::vector empty_floating_field_; + std::tr1::shared_ptr empty_special_field_; + EclipseUnits units_; +}; + + +} // namespace Dune + +#endif // SINTEF_ECLIPSEGRIDPARSER_HEADER diff --git a/opm/core/eclipse/EclipseGridParserHelpers.hpp b/opm/core/eclipse/EclipseGridParserHelpers.hpp new file mode 100644 index 00000000..382b0a90 --- /dev/null +++ b/opm/core/eclipse/EclipseGridParserHelpers.hpp @@ -0,0 +1,469 @@ +//=========================================================================== +// +// File: EclipseGridParserHelpers.hpp +// +// Created: Tue Dec 22 11:35:32 2009 +// +// Author(s): Atgeirr F Rasmussen +// Bård Skaflestad +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* + Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. + Copyright 2009, 2010 Statoil ASA. + + This file is part of The Open Reservoir Simulator Project (OpenRS). + + OpenRS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenRS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenRS. If not, see . +*/ + +#ifndef OPENRS_ECLIPSEGRIDPARSERHELPERS_HEADER +#define OPENRS_ECLIPSEGRIDPARSERHELPERS_HEADER + +#include +#include +#include +#include +#include +#include +#include + +namespace Dune +{ + +namespace +{ + + inline std::istream& ignoreLine(std::istream& is) + { + is.ignore(std::numeric_limits::max(), '\n'); + return is; + } + + inline std::istream& ignoreSlashLine(std::istream& is) + { + is.ignore(std::numeric_limits::max(), '/'); + is.ignore(std::numeric_limits::max(), '\n'); + return is; + } + + inline std::istream& ignoreWhitespace(std::istream& is) + { + // Getting the character type facet for is() + // We use the classic (i.e. C) locale. + const std::ctype& ct = std::use_facet< std::ctype >(std::locale::classic()); + char c; + while (is.get(c)) { + if (!ct.is(std::ctype_base::space, c)) { + is.putback(c); + break; + } + } + return is; + } + + inline std::string upcase(const std::string& s) + { + std::string us(s); + // Getting the character type facet for toupper(). + // We use the classic (i.e. C) locale. + const std::ctype& ct = std::use_facet< std::ctype >(std::locale::classic()); + for (int i = 0; i < int(s.size()); ++i) { + us[i] = ct.toupper(s[i]); + } + return us; + } + + inline std::string readKeyword(std::istream& is) + { + std::string keyword_candidate; + while (!is.eof()) { + is >> keyword_candidate; + if(keyword_candidate.find("--") == 0) { + is >> ignoreLine; // This line is a comment + } else { + return upcase(keyword_candidate); + } + } + return "CONTINUE"; // Last line in included file is a comment + } + + inline std::string readString(std::istream& is) + { + std::string string_candidate; + is >> string_candidate; + const char quote('\''); + int beg = string_candidate[0] == quote ? 1 : 0; + int len = string_candidate[0] == quote ? string_candidate.size() - 2 : string_candidate.size(); + return string_candidate.substr(beg, len); + } + + // Reads data until '/' or an error is encountered. + template + inline void readVectorData(std::istream& is, std::vector& data) + { + data.clear(); + while (is) { + T candidate; + is >> candidate; + if (is.rdstate() & std::ios::failbit) { + is.clear(is.rdstate() & ~std::ios::failbit); + is >> ignoreWhitespace; + char dummy; + is >> dummy; + if (dummy == '/') { + is >> ignoreLine; + break; + } else if (dummy == '-') { // "comment test" + is >> ignoreLine; // This line is a comment + } else { + THROW("Encountered format error while reading data values. Value = " << dummy); + } + } else { + if (is.peek() == int('*')) { + is.ignore(); // ignore the '*' + int multiplier = int(candidate); + is >> candidate; + data.insert(data.end(), multiplier, candidate); + } else { + data.push_back(candidate); + } + } + } + if (!is) { + THROW("Encountered error while reading data values."); + } + } + + + // Reads data items of type T. Not more than 'max_values' items. + // Asterisks may be used to signify 'repeat counts'. 5*3.14 will + // insert 3.14 five times. Asterisk followed by a space is used to + // signify default values. n* will default n consecutive quantities. + template + inline int readDefaultedVectorData(std::istream& is, Vec& data, int max_values) + { + ASSERT(int(data.size()) >= max_values); + const std::ctype& ct = std::use_facet< std::ctype >(std::locale::classic()); + int num_values = 0; + while (is) { + typename Vec::value_type candidate; + is >> candidate; + if (is.rdstate() & std::ios::failbit) { + is.clear(is.rdstate() & ~std::ios::failbit); + std::string dummy; + is >> dummy; + if (dummy == "/") { + is >> ignoreLine; + break; + } else if (dummy[0] == '-') { // "comment test" + is >> ignoreLine; // This line is a comment + } else { + THROW("Encountered format error while reading data values. Value = " << dummy); + } + } else { + if (is.peek() == int('*')) { + is.ignore(); // ignore the '*' + int multiplier = (int)candidate; + if (ct.is(std::ctype_base::space, is.peek())) { + num_values += multiplier; // Use default value(s) + } else { + is >> candidate; // Use candidate 'multipler' times + for (int i=0; i= max_values) { + //is >> ignoreLine; + break; + } + } + if (!is) { + THROW("Encountered error while reading data values."); + } + return num_values; + } + + + // Keywords SGOF and SWOF. Reads data until '/' or an error is encountered. + // Default values represented by 1* is replaced by -1. Use linear interpolation + // outside this function to replace -1. + template + inline void readRelPermTable(std::istream& is, std::vector& data) + { + data.clear(); + while (is) { + T candidate; + is >> candidate; + if (is.rdstate() & std::ios::failbit) { + is.clear(is.rdstate() & ~std::ios::failbit); + std::string dummy; + is >> dummy; + if (dummy == "/") { + is >> ignoreLine; + break; + } else if (dummy[0] == '-') { // "comment test" + is >> ignoreLine; // This line is a comment + } else { + THROW("Encountered format error while reading data values. Value = " << dummy); + } + } else { + if (is.peek() == int('*')) { + is.ignore(); // ignore the '*' + ASSERT(int(candidate) == 1); + data.push_back(-1); // Set new flag for interpolation. + } else { + data.push_back(candidate); + } + } + } + if (!is) { + THROW("Encountered error while reading data values."); + } + } + + + // Returns month number 1-12. Returns 0 if illegal month name. + inline int getMonthNumber(const std::string& month_name) + { + const int num_months = 12; + std::string months[num_months] = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN", + "JLY", "AUG", "SEP", "OCT", "NOV", "DEC"}; + if (month_name == "JUL") { + return 7; // "JUL" is an acceptable alternative to 'JLY' + } + int m = 0; + for (int i=0; i> ignoreLine; // This line is a comment + } + int day, year; + std::string month_name; + is >> day; + month_name = readString(is); + is >> year; + ignoreSlashLine(is); + int month = getMonthNumber(month_name); + return boost::gregorian::date(boost::gregorian::greg_year(year), + boost::gregorian::greg_month(month), + boost::gregorian::greg_day(day)); + } + + // Get first character after whitespace and comments, and decide + // next action. + // NB! Will fail for negative number in column one. + inline int next_action(std::istream& is) + { + int task(0); // 0:continue 1:return 2:throw + const std::ctype& ct = + std::use_facet< std::ctype >(std::locale::classic()); + + while (!is.eof()) { + is >> ignoreWhitespace; + char c; + is.get(c); + if (is.eof()) { + return task; + } + is.putback(c); + if (ct.is(std::ctype_base::digit, c) || c== '.') { + task = 0; // Decimal digit. Read more data. + break; + } + if (ct.is(std::ctype_base::alpha, c)) { + task = 1; // Alphabetic char. Read next keyword. + break; + } + if (c == '-') { + is >> ignoreLine; // This line is a comment + } else { + task = 2; // Read error. Unexpected character. + break; + } + } + return task; + } + + // Reads keywords PVTG, PVTO + typedef std::vector > > table_t; + inline void readPvtTable(std::istream& is, table_t& pvt_table, + const std::string& field_name) + { + const std::ctype& ct = + std::use_facet< std::ctype >(std::locale::classic()); + std::vector record; + std::vector > table; + while (!is.eof()) { + record.clear(); + readVectorData(is, record); + table.push_back(record); + while (!is.eof()) { + is >> ignoreWhitespace; + char c; + is.get(c); + if (is.eof()) { + // Reached end of file, we should have pushed + // the last table, and emptied it. If not, + // we have an error. + if (!table.empty()) { + THROW("Reached EOF while still building PVT table. Missing end-of-table (slash)?"); + } + return; + } + is.putback(c); + if (ct.is(std::ctype_base::digit, c) || c== '.') { + break; // Decimal digit. Read more records. + } + if (ct.is(std::ctype_base::alpha, c)) { + return; // Alphabetic char. Read next keyword. + } + if (c == '-') { + is >> ignoreLine; // This line is a comment + continue; + } + if (c == '/') { + is >> ignoreLine; + pvt_table.push_back(table); + table.clear(); + } else { + std::ostringstream oss; + oss << "Error reading " << field_name + << ". Next character is " << (char)is.peek(); + THROW(oss.str()); + } + } + } + } + + // Reads keywords PVDG, PVDO, ROCKTAB + inline void readPvdTable(std::istream& is, table_t& pvd_table, + const std::string& field_name, int ncol) + { + std::vector record; + std::vector > table(ncol); + while (!is.eof()) { + record.clear(); + readVectorData(is, record); + const int rec_size = record.size()/ncol; + for (int k=0; k >& table, int ncol) + { + const int sz = table[0].size(); + for (int k=1; k indx; + std::vector x; + for (int i=0; i xv, yv; + for (int i=0; i record; + std::vector > table(ncol); + while (!is.eof()) { + record.clear(); + readRelPermTable(is, record); + const int rec_size = record.size()/ncol; + for (int k=0; k. +*/ + +#ifndef OPM_ECLIPSEUNITS_HEADER_INCLUDED +#define OPM_ECLIPSEUNITS_HEADER_INCLUDED + +namespace Dune +{ + struct EclipseUnits + { + double length; + double time; + double density; + double pressure; + double compressibility; + double viscosity; + double permeability; + double liqvol_s; + double liqvol_r; + double gasvol_s; + double gasvol_r; + double transmissibility; + + void setToOne() + { + length = 1.0; + time = 1.0; + density = 1.0; + pressure = 1.0; + compressibility = 1.0; + viscosity = 1.0; + permeability = 1.0; + liqvol_s = 1.0; + liqvol_r = 1.0; + gasvol_s = 1.0; + gasvol_r = 1.0; + transmissibility = 1.0; + } + }; + + +} // namespace Dune + + +#endif // OPM_ECLIPSEUNITS_HEADER_INCLUDED diff --git a/opm/core/eclipse/SpecialEclipseFields.hpp b/opm/core/eclipse/SpecialEclipseFields.hpp new file mode 100644 index 00000000..81373ef3 --- /dev/null +++ b/opm/core/eclipse/SpecialEclipseFields.hpp @@ -0,0 +1,1473 @@ +//=========================================================================== +// +// File: SpecialEclipseFields.hpp +// +// Created: Mon Sep 21 14:09:54 2009 +// +// Author(s): Atgeirr F Rasmussen +// BÃ¥rd Skaflestad +// Bjørn Spjelkavik +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* + Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. + Copyright 2009, 2010 Statoil ASA. + + This file is part of The Open Reservoir Simulator Project (OpenRS). + + OpenRS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenRS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenRS. If not, see . +*/ + +#ifndef OPENRS_SPECIALECLIPSEFIELDS_HEADER +#define OPENRS_SPECIALECLIPSEFIELDS_HEADER + +#include +#include +#include +#include +#include +#include + +namespace Dune +{ + +// Abstract base class for special fields. +struct SpecialBase { + virtual ~SpecialBase() {} // Default destructor + //virtual std::string name() const = 0; // Keyword name + virtual void read(std::istream& is) = 0; // Reads data + //virtual void write(std::ostream& os) const = 0; // Writes data + virtual void convertToSI(const EclipseUnits&) + { + THROW("Default conversion not defined."); + } + typedef std::vector > > table_t; +}; + + + + +/// Class for keyword SPECGRID +struct SPECGRID : public SpecialBase +{ + std::vector dimensions; // Number of grid blocks in x-, y- and z-directions. + int numres; // Number of reservoirs. + char qrdial; // Coordinates. F=cartesian, T=Cylindrical(radial). + + SPECGRID() + { + dimensions.resize(3,1); + numres = 1; + qrdial = 'F'; + } + + virtual ~SPECGRID() + { + } + + virtual std::string name() const {return std::string("SPECGRID");} + + virtual void read(std::istream& is) + { + const int ndim = 3; + std::vector data(ndim+1,1); + int nread = readDefaultedVectorData(is , data, ndim+1); + int nd = std::min(nread, ndim); + copy(data.begin(), data.begin()+nd, &dimensions[0]); + numres = data[ndim]; + std::string candidate; + is >> candidate; + if (candidate == "/") { + return; + } else { + qrdial = candidate[0]; + } + + if (ignoreSlashLine(is)) { + return; + } else { + THROW("End of file reading" << name()); + } + } + + virtual void write(std::ostream& os) const + { + os << name() << std::endl; + os << dimensions[0] << " " << dimensions[1] << " " + << dimensions[2] << " " << numres << " " << qrdial << std::endl; + os << std::endl; + } + + virtual void convertToSI(const EclipseUnits&) + {} +}; + + + + +/// Class holding segment data of keyword FAULTS. +struct FaultSegment +{ + std::string fault_name; // Fault name + std::vector ijk_coord; // ijk-coordinates of segment cells + std::string face; // Fault face of cells +}; + +/// Class for keyword FAULTS. +struct FAULTS : public SpecialBase +{ + std::vector faults; + + FAULTS() + { + } + + virtual ~FAULTS() + { + } + + virtual std::string name() const {return std::string("FAULTS");} + + virtual void read(std::istream& is) + { + while(is) { + std::string name; + is >> name; + if (name[0] == '/') { + is >> ignoreLine; + break; + } + while (name.find("--") == 0) { + // This line is a comment + is >> ignoreLine >> name; + } + FaultSegment fault_segment; + fault_segment.ijk_coord.resize(6); + fault_segment.fault_name = name; + int nread = readDefaultedVectorData(is, fault_segment.ijk_coord, 6); + if (nread != 6) { + THROW("Error reading fault_segment " << name); + } + is >> fault_segment.face; + faults.push_back(fault_segment); + ignoreSlashLine(is); + } + } + + virtual void write(std::ostream& os) const + { + os << name() << std::endl; + for (int i=0; i<(int)faults.size(); ++i) { + os << faults[i].fault_name << " "; + copy(faults[i].ijk_coord.begin(), faults[i].ijk_coord.end(), + std::ostream_iterator(os, " ")); + os << faults[i].face << std::endl; + } + os << std::endl; + } + + virtual void convertToSI(const EclipseUnits&) + {} +}; + + + + +/// Class holding a data line of keyword MULTFLT +struct MultfltLine +{ + std::string fault_name; // Fault name, as in FAULTS + double transmis_multiplier; // Transmissibility multiplier + double diffusivity_multiplier; // Diffusivity multiplier; + MultfltLine() : + fault_name(""), transmis_multiplier(1.0), diffusivity_multiplier(1.0) + {} +}; + +/// Class for keyword MULFLT +struct MULTFLT : public SpecialBase +{ + std::vector multflts; + + MULTFLT() + { + } + + virtual ~MULTFLT() + {} + + virtual std::string name() const {return std::string("MULTFLT");} + + virtual void read(std::istream& is) + { + while(is) { + std::string name; + is >> name; + if (name[0] == '/') { + is >> ignoreLine; + break; + } + while (name == "--") { + // This line is a comment + is >> ignoreLine >> name; + } + MultfltLine multflt_line; + multflt_line.fault_name = name; + std::vector data(2,1.0); + if (readDefaultedVectorData(is, data, 2) == 2) { + ignoreSlashLine(is); + } + multflt_line.transmis_multiplier = data[0]; + multflt_line.diffusivity_multiplier = data[1]; + multflts.push_back(multflt_line); + } + } + + virtual void write(std::ostream& os) const + { + os << name() << std::endl; + for (int i=0; i<(int)multflts.size(); ++i) { + os << multflts[i].fault_name << " " + << multflts[i].transmis_multiplier << " " + << multflts[i].diffusivity_multiplier << std::endl; + } + os << std::endl; + } + + virtual void convertToSI(const EclipseUnits&) + {} +}; + + + + +struct TITLE : public SpecialBase +{ + std::string title; + virtual std::string name() const + { return std::string("TITLE"); } + virtual void read(std::istream& is) + { is >> ignoreLine; std::getline(is, title); } + virtual void write(std::ostream& os) const + { os << name() << '\n' << title << '\n'; } + virtual void convertToSI(const EclipseUnits&) + {} +}; + + + + +struct START : public SpecialBase +{ + boost::gregorian::date date; + virtual std::string name() const + { return std::string("START"); } + virtual void read(std::istream& is) + { date = readDate(is); } + virtual void write(std::ostream& os) const + { os << name() << '\n' << date << '\n'; } + virtual void convertToSI(const EclipseUnits&) + {} +}; + + + + +struct DATES : public SpecialBase +{ + std::vector dates; + virtual std::string name() const + { return std::string("DATES"); } + virtual void read(std::istream& is) + { + while(is) { + dates.push_back(readDate(is)); + is >> ignoreWhitespace; + if (is.peek() == int('/')) { + is >> ignoreLine; + break; + } + } + } + virtual void write(std::ostream& os) const + { + os << name() << '\n'; + copy(dates.begin(), dates.end(), + std::ostream_iterator(os, "\n")); + } + virtual void convertToSI(const EclipseUnits&) + {} +}; + + +struct DENSITY : public SpecialBase +{ + std::vector > densities_; + + virtual std::string name() const {return std::string("DENSITY");} + + virtual void read(std::istream& is) + { + while (!is.eof()) { + std::vector density(3,-1e100); + if (readDefaultedVectorData(is, density, 3) == 3) { + ignoreSlashLine(is); + } + densities_.push_back(density); + + int action = next_action(is); // 0:continue 1:return 2:throw + if (action == 1) { + return; // Alphabetic char. Read next keyword. + } else if (action == 2) { + THROW("Error reading DENSITY. Next character is " + << (char)is.peek()); + } + } + } + + virtual void write(std::ostream& os) const + { + os << name() << '\n'; + for (int i=0; i<(int)densities_.size(); ++i) { + os << densities_[i][0] << " " << densities_[i][1] << " " + << densities_[i][2] << '\n'; + } + os << '\n'; + } + + virtual void convertToSI(const EclipseUnits& units) + { + for (int i=0; i<(int)densities_.size(); ++i) { + densities_[i][0] *= units.density; + densities_[i][1] *= units.density; + densities_[i][2] *= units.density; + } + } +}; + +struct PVDG : public SpecialBase +{ + table_t pvdg_; + + virtual std::string name() const {return std::string("PVDG");} + + virtual void read(std::istream& is) + { + readPvdTable(is, pvdg_, name(), 3); + } + + virtual void write(std::ostream& os) const + { + os << name() << '\n'; + for (int rn=0; rn<(int)pvdg_.size(); ++rn) { + for (int i=0; i<(int)pvdg_[rn][0].size(); ++i) { + os << pvdg_[rn][0][i] << " " << pvdg_[rn][1][i] << " " + << pvdg_[rn][2][i] << '\n'; + } + os << '\n'; + } + os << '\n'; + } + + virtual void convertToSI(const EclipseUnits& units) + { + double volfac = units.gasvol_r/units.gasvol_s; + for (int rn=0; rn<(int)pvdg_.size(); ++rn) { + for (int i=0; i<(int)pvdg_[rn][0].size(); ++i) { + pvdg_[rn][0][i] *= units.pressure; + pvdg_[rn][1][i] *= volfac; + pvdg_[rn][2][i] *= units.viscosity; + } + } + } +}; + +struct PVDO : public SpecialBase +{ + table_t pvdo_; + + virtual std::string name() const {return std::string("PVDO");} + + virtual void read(std::istream& is) + { + readPvdTable(is, pvdo_, name(), 3); + } + + virtual void write(std::ostream& os) const + { + os << name() << '\n'; + for (int rn=0; rn<(int)pvdo_.size(); ++rn) { + for (int i=0; i<(int)pvdo_[rn][0].size(); ++i) { + os << pvdo_[rn][0][i] << " " << pvdo_[rn][1][i] << " " + << pvdo_[rn][2][i] << '\n'; + } + os << '\n'; + } + os << '\n'; + } + + virtual void convertToSI(const EclipseUnits& units) + { + double volfac = units.liqvol_r/units.liqvol_s; + for (int rn=0; rn<(int)pvdo_.size(); ++rn) { + for (int i=0; i<(int)pvdo_[rn][0].size(); ++i) { + pvdo_[rn][0][i] *= units.pressure; + pvdo_[rn][1][i] *= volfac; + pvdo_[rn][2][i] *= units.viscosity; + } + } + } +}; + +struct PVTG : public SpecialBase +{ + table_t pvtg_; + + virtual std::string name() const {return std::string("PVTG");} + + virtual void read(std::istream& is) + { + readPvtTable(is, pvtg_, name()); + } + + virtual void write(std::ostream& os) const + { + os << name() << '\n'; + for (int rn=0; rn<(int)pvtg_.size(); ++rn) { + for (int i=0; i<(int)pvtg_[rn].size(); ++i) { + int nl = (pvtg_[rn][i].size()-1) / 3; + os << pvtg_[rn][i][0] << " " << pvtg_[rn][i][1] << " " + << pvtg_[rn][i][2] << " " << pvtg_[rn][i][3] << '\n'; + for (int j=1, n=3; j > pvtw_; + + virtual std::string name() const {return std::string("PVTW");} + + virtual void read(std::istream& is) + { + while (!is.eof()) { + std::vector pvtw; + readVectorData(is, pvtw); + if (pvtw.size() == 4) { + pvtw.push_back(0.0); // Not used by frontsim + } + pvtw_.push_back(pvtw); + + int action = next_action(is); // 0:continue 1:return 2:throw + if (action == 1) { + return; // Alphabetic char. Read next keyword. + } else if (action == 2) { + THROW("Error reading PVTW. Next character is " + << (char)is.peek()); + } + } + } + + virtual void write(std::ostream& os) const + { + os << name() << '\n'; + for (int i=0; i<(int)pvtw_.size(); ++i) { + os << pvtw_[i][0] << " " << pvtw_[i][1] << " " << pvtw_[i][2] + << " " << pvtw_[i][3] << " " << pvtw_[i][4] << '\n'; + } + os << '\n'; + } + + virtual void convertToSI(const EclipseUnits& units) + { + double volfac = units.liqvol_r/units.liqvol_s; + for (int i=0; i<(int)pvtw_.size(); ++i) { + pvtw_[i][0] *= units.pressure; + pvtw_[i][1] *= volfac; + pvtw_[i][2] *= units.compressibility; + pvtw_[i][3] *= units.viscosity; + pvtw_[i][4] *= units.compressibility; + } + } +}; + + +struct ROCK : public SpecialBase +{ + std::vector > rock_compressibilities_; + + virtual std::string name() const {return std::string("ROCK");} + + virtual void read(std::istream& is) + { + while (!is.eof()) { + std::vector rock; + readVectorData(is, rock); + rock_compressibilities_.push_back(rock); + + int action = next_action(is); // 0:continue 1:return 2:throw + if (action == 1) { + return; // Alphabetic char. Read next keyword. + } else if (action == 2) { + THROW("Error reading ROCK. Next character is " + << (char)is.peek()); + } + } + } + + virtual void write(std::ostream& os) const + { + os << name() << '\n'; + for (int i=0; i<(int)rock_compressibilities_.size(); ++i) { + os << rock_compressibilities_[i][0] << " " + << rock_compressibilities_[i][1] << '\n'; + } + os << '\n'; + } + + virtual void convertToSI(const EclipseUnits& units) + { + for (int i=0; i<(int)rock_compressibilities_.size(); ++i) { + rock_compressibilities_[i][0] *= units.pressure; + rock_compressibilities_[i][1] *= units.compressibility; + } + } +}; + + +struct ROCKTAB : public SpecialBase +{ + table_t rocktab_; + + virtual std::string name() const {return std::string("ROCKTAB");} + + virtual void read(std::istream& is) + { + readPvdTable(is, rocktab_, name(), 3); + } + + virtual void write(std::ostream& os) const + { + os << name() << '\n'; + for (int rn=0; rn<(int)rocktab_.size(); ++rn) { + for (int i=0; i<(int)rocktab_[rn][0].size(); ++i) { + os << rocktab_[rn][0][i] << " " << rocktab_[rn][1][i] << " " + << rocktab_[rn][2][i] << '\n'; + } + os << '\n'; + } + os << '\n'; + } + + virtual void convertToSI(const EclipseUnits& units) + { + for (int rn=0; rn<(int)rocktab_.size(); ++rn) { + for (int i=0; i<(int)rocktab_[rn][0].size(); ++i) { + rocktab_[rn][0][i] *= units.pressure; + } + } + } +}; + + +struct SGOF : public SpecialBase +{ + table_t sgof_; + + virtual std::string name() const {return std::string("SGOF");} + + virtual void read(std::istream& is) {readSGWOF(is, sgof_, name(), 4);} + + virtual void write(std::ostream& os) const + { + os << name() << '\n'; + for (int rn=0; rn<(int)sgof_.size(); ++rn) { + for (int i=0; i<(int)sgof_[rn][0].size(); ++i) { + os << sgof_[rn][0][i] << " " << sgof_[rn][1][i] << " " + << sgof_[rn][2][i] << " " << sgof_[rn][3][i] << '\n'; + } + os << '\n'; + } + os << '\n'; + } + + virtual void convertToSI(const EclipseUnits& units) + { + for (int rn=0; rn<(int)sgof_.size(); ++rn) { + for (int i=0; i<(int)sgof_[rn][0].size(); ++i) { + sgof_[rn][3][i] *= units.pressure; + } + } + } +}; + +struct SWOF : public SpecialBase +{ + table_t swof_; + + virtual std::string name() const {return std::string("SWOF");} + + virtual void read(std::istream& is) {readSGWOF(is, swof_, name(), 4);} + + virtual void write(std::ostream& os) const + { + os << name() << '\n'; + for (int rn=0; rn<(int)swof_.size(); ++rn) { + for (int i=0; i<(int)swof_[rn][0].size(); ++i) { + os << swof_[rn][0][i] << " " << swof_[rn][1][i] << " " + << swof_[rn][2][i] << " " << swof_[rn][3][i] << '\n'; + } + os << '\n'; + } + os << '\n'; + } + + virtual void convertToSI(const EclipseUnits& units) + { + for (int rn=0; rn<(int)swof_.size(); ++rn) { + for (int i=0; i<(int)swof_[rn][0].size(); ++i) { + swof_[rn][3][i] *= units.pressure; + } + } + } +}; + +/// Class holding a data line of keyword WELSPECS +struct WelspecsLine +{ + std::string name_; // Well name + std::string group_; // Group name + int I_; // I-location of well head or heel + int J_; // J-location of well head or heel + double datum_depth_BHP_; // Datum depth for bottom hole pressure + std::string pref_phase_; // Preferred phase for the well + double drain_rad_; // Drainage radius for prod/inj index calculation + std::string spec_inflow_; // Flag for special inflow equation + std::string shut_in_; // Instructions for automatic shut-in + std::string crossflow_; // Crossflow ability flag + int pressure_table_number_; // Pressure table number for wellbore fluid properties + std::string density_calc_type_; // Type of density calculation for wellbore hydrostatic head + int fluids_in_place_reg_numb_; // Fluids in place region number + + WelspecsLine() : + datum_depth_BHP_(-1.0), drain_rad_(0.0), spec_inflow_("STD"), + shut_in_("SHUT"), crossflow_("YES"), pressure_table_number_(0), + density_calc_type_("SEG"), fluids_in_place_reg_numb_(0) + {} +}; + +/// Class for keyword WELSPECS +struct WELSPECS : public SpecialBase +{ + std::vector welspecs; + + WELSPECS() + { + } + + virtual ~WELSPECS() + {} + + virtual std::string name() const {return std::string("WELSPECS");} + + virtual void read(std::istream& is) + { + while(is) { + std::string name = readString(is); + if (name[0] == '/') { + is >> ignoreLine; + break; + } + while (name.find("--") == 0) { + // This line is a comment + is >> ignoreLine; + name = readString(is); + } + WelspecsLine welspecs_line; + welspecs_line.name_ = name; + welspecs_line.group_ = readString(is); + std::vector int_data(2,1); + readDefaultedVectorData(is, int_data, 2); + welspecs_line.I_ = int_data[0]; + welspecs_line.J_ = int_data[1]; + std::vector double_data(1,-1.0); + readDefaultedVectorData(is, double_data, 1); + welspecs_line.datum_depth_BHP_ = double_data[0]; + welspecs_line.pref_phase_ = readString(is); + + // HACK! Ignore items 7-13. + ignoreSlashLine(is); + welspecs.push_back(welspecs_line); + + // double_data[0] = 0.0; + // readDefaultedVectorData(is, double_data, 1); + // welspecs_line.drain_rad_ = double_data[0]; + // welspecs_line.spec_inflow_ = readString(is); + // welspecs_line.shut_in_ = readString(is); + // welspecs_line.crossflow_ = readString(is); + // int_data[0] = 0; + // readDefaultedVectorData(is, int_data, 1); + // welspecs_line.pressure_table_number_ = int_data[0]; + // welspecs_line.density_calc_type_ = readString(is); + // int_data[0] = 0; + // readDefaultedVectorData(is, int_data, 1); + // welspecs_line.fluids_in_place_reg_numb_ = int_data[0]; + // welspecs.push_back(welspecs_line); + } + } + + virtual void write(std::ostream& os) const + { + os << name() << std::endl; + for (int i=0; i<(int)welspecs.size(); ++i) { + os << welspecs[i].name_ << " " + << welspecs[i].group_ << " " + << welspecs[i].I_ << " " + << welspecs[i].J_ << " " + << welspecs[i].datum_depth_BHP_ << " " + << welspecs[i].pref_phase_ << " " + << welspecs[i].drain_rad_ << " " + << welspecs[i].shut_in_ << " " + << welspecs[i].crossflow_ << " " + << welspecs[i].pressure_table_number_ << " " + << welspecs[i].density_calc_type_ << " " + << welspecs[i].fluids_in_place_reg_numb_ << " " + << std::endl; + } + os << std::endl; + } + + virtual void convertToSI(const EclipseUnits& units) + { + for (int i=0; i<(int)welspecs.size(); ++i) { + welspecs[i].datum_depth_BHP_ *= units.length; + welspecs[i].drain_rad_ *= units.length; + } + } +}; + +/// Class holding a data line of keyword COMPDAT +struct CompdatLine +{ + std::string well_; // Well name + std::vector grid_ind_; // Grid block location + std::string open_shut_flag_; // Open/shut flag of connection + int sat_table_number_; // Saturation table number + double connect_trans_fac_; // Connection transmillibilty factor + double diameter_; // Well bore internal diameter + double Kh_; // Effective Kh value of the connection + double skin_factor_; // Skin factor + double D_factor_; // D-factor, for non-Darcy flow of free gas + std::string penetration_direct_; // Penetration direction + double r0_; // Pressure equivalent radius + + // Default values + CompdatLine() : + open_shut_flag_("OPEN"), + sat_table_number_(0), + connect_trans_fac_(0.0), + diameter_(0.0), + Kh_(-1.0), + skin_factor_(0.0), + D_factor_(-1e100), + penetration_direct_("Z"), + r0_(0.0) + { + grid_ind_.resize(4); + } +}; + +/// Class for keyword COMPDAT +struct COMPDAT : public SpecialBase +{ + std::vector compdat; + + COMPDAT() + { + } + + virtual ~COMPDAT() + {} + + virtual std::string name() const {return std::string("COMPDAT");} + + virtual void read(std::istream& is) + { + while(is) { + std::string name = readString(is); + if (name[0] == '/') { + is >> ignoreLine; + break; + } + while (name.find("--") == 0) { + // This line is a comment + is >> ignoreLine; + name = readString(is); + } + CompdatLine compdat_line; + compdat_line.well_ = name; + readDefaultedVectorData(is, compdat_line.grid_ind_, 4); + compdat_line.open_shut_flag_ = readString(is); + std::vector int_data(1,-1); + readDefaultedVectorData(is, int_data, 1); + compdat_line.sat_table_number_ = int_data[0]; + std::vector double_data(2, 0.0); + int num_to_read = 2; + int num_read = readDefaultedVectorData(is, double_data, num_to_read); + compdat_line.connect_trans_fac_ = double_data[0]; + compdat_line.diameter_ = double_data[1]; + + // HACK! Ignore items 10-14. + if (num_read == num_to_read) { + ignoreSlashLine(is); + } + compdat.push_back(compdat_line); + } + } + + virtual void write(std::ostream& os) const + { + os << name() << std::endl; + for (int i=0; i<(int)compdat.size(); ++i) { + os << compdat[i].well_ << " " + << compdat[i].grid_ind_[0] << " " + << compdat[i].grid_ind_[1] << " " + << compdat[i].grid_ind_[2] << " " + << compdat[i].grid_ind_[3] << " " + << compdat[i].open_shut_flag_ << " " + << compdat[i].sat_table_number_ << " " + << compdat[i].connect_trans_fac_ << " " + << compdat[i].diameter_ << " " + << compdat[i].Kh_ << " " + << compdat[i].skin_factor_ << " " + << compdat[i].D_factor_ << " " + << compdat[i].penetration_direct_ << " " + << compdat[i].r0_ + << std::endl; + } + os << std::endl; + } + + virtual void convertToSI(const EclipseUnits& units) + { + for (int i=0; i<(int)compdat.size(); ++i) { + compdat[i].connect_trans_fac_ *= units.transmissibility; + compdat[i].diameter_ *= units.length; + compdat[i].Kh_ *= units.permeability*units.length; + compdat[i].r0_ *= units.length; + } + } +}; + +/// Class holding a data line of keyword WCONINJE +struct WconinjeLine +{ + std::string well_; // Well name or well name root + std::string injector_type_; // Injector type + std::string open_shut_flag_; // Open/shut flag for the well + std::string control_mode_; // Control mode + double surface_flow_max_rate_; // Surface flow rate target or upper limit + double fluid_volume_max_rate_; // Reservoir fluid volume rate target or + // upper limit + double BHP_limit_; // BHP target or upper limit + double THP_limit_; // THP target or upper limit + int VFP_table_number_; // Injection well VFP table number + double concentration_; // Vaporised oil concentration in the + // injected gas, or dissolved gas + // concentration in the injected oil + + // Default values + WconinjeLine() : + open_shut_flag_("OPEN"), surface_flow_max_rate_(1.0E20), + fluid_volume_max_rate_(1.0E20), BHP_limit_(6895), THP_limit_(1.0E20), + VFP_table_number_(0), concentration_(0.0) + { + } +}; + +/// Class for keyword WCONINJE +struct WCONINJE : public SpecialBase +{ + std::vector wconinje; + + WCONINJE() + { + } + + virtual ~WCONINJE() + {} + + virtual std::string name() const {return std::string("WCONINJE");} + + virtual void read(std::istream& is) + { + while(is) { + std::string name = readString(is); + if (name[0] == '/') { + is >> ignoreLine; + break; + } + while (name.find("--") == 0) { + // This line is a comment + is >> ignoreLine; + name = readString(is); + } + WconinjeLine wconinje_line; + wconinje_line.well_ = name; + wconinje_line.injector_type_ = readString(is); + wconinje_line.open_shut_flag_ = readString(is); + wconinje_line.control_mode_ = readString(is); + std::vector double_data(6, 1.0E20); + double_data[2] = wconinje_line.BHP_limit_; + double_data[4] = wconinje_line.VFP_table_number_; + double_data[5] = wconinje_line.concentration_; + readDefaultedVectorData(is, double_data, 6); + wconinje_line.surface_flow_max_rate_ = double_data[0]; + wconinje_line.fluid_volume_max_rate_ = double_data[1]; + wconinje_line.BHP_limit_ = double_data[2]; + wconinje_line.THP_limit_ = double_data[3]; + wconinje_line.VFP_table_number_ = (int)double_data[4]; + wconinje_line.concentration_ = double_data[5]; + wconinje.push_back(wconinje_line); + } + } + + virtual void write(std::ostream& os) const + { + os << name() << std::endl; + for (int i=0; i<(int) wconinje.size(); ++i) { + os << wconinje[i].well_ << " " + << wconinje[i].injector_type_ << " " + << wconinje[i].open_shut_flag_ << " " + << wconinje[i].control_mode_ << " " + << wconinje[i].surface_flow_max_rate_ << " " + << wconinje[i].fluid_volume_max_rate_ << " " + << wconinje[i].BHP_limit_ << " " + << wconinje[i].THP_limit_ << " " + << wconinje[i].VFP_table_number_ << " " + << wconinje[i].concentration_ + << std::endl; + } + os << std::endl; + } + + virtual void convertToSI(const EclipseUnits& units) + { + for (int i=0; i<(int) wconinje.size(); ++i) { + if (wconinje[i].injector_type_ == "GAS") { + wconinje[i].surface_flow_max_rate_ *= units.gasvol_s/units.time; + } else { + wconinje[i].surface_flow_max_rate_ *= units.liqvol_s/units.time; + } + wconinje[i].fluid_volume_max_rate_ *= units.liqvol_r/units.time; + wconinje[i].BHP_limit_ *= units.pressure; + wconinje[i].THP_limit_ *= units.pressure; + wconinje[i].concentration_ *= units.gasvol_s/units.liqvol_s; // ??? @bsp 10 + } + } +}; + +/// Class holding a data line of keyword WCONPROD +struct WconprodLine +{ + std::string well_; // Well name or well name root + std::string open_shut_flag_; // Open/shut flag for the well + std::string control_mode_; // Control mode + double oil_max_rate_; // Oil rate target or upper limit + double water_max_rate_; // Water rate target or upper limit + double gas_max_rate_; // Gas rate target or upper limit + double liquid_max_rate_; // Liquid rate target or upper limit + double fluid_volume_max_rate_; // Reservoir fluid volume rate target or + // upper limit + double BHP_limit_; // BHP target or upper limit + double THP_limit_; // THP target or upper limit + int VFP_table_number_; // Injection well VFP table number + double artif_lift_quantity_; // Artificial lift quantity in THP calculations + + // Default values + WconprodLine() : + open_shut_flag_("OPEN"), oil_max_rate_(1.0E20), water_max_rate_(1.0E20), + gas_max_rate_(1.0E20), liquid_max_rate_(1.0E20), + fluid_volume_max_rate_(1.0E20), BHP_limit_(-1.0), THP_limit_(0.0), + VFP_table_number_(0), artif_lift_quantity_(0.0) + { + } +}; + +/// Class for keyword WCONPROD +struct WCONPROD : public SpecialBase +{ + std::vector wconprod; + + WCONPROD() + { + } + + virtual ~WCONPROD() + {} + + virtual std::string name() const {return std::string("WCONPROD");} + + virtual void read(std::istream& is) + { + while(is) { + std::string name = readString(is); + if (name[0] == '/') { + is >> ignoreLine; + break; + } + while (name.find("--") == 0) { + // This line is a comment + is >> ignoreLine; + name = readString(is); + } + WconprodLine wconprod_line; + wconprod_line.well_ = name; + wconprod_line.open_shut_flag_ = readString(is); + wconprod_line.control_mode_ = readString(is); + std::vector double_data(9, 1.0E20); + double_data[5] = wconprod_line.BHP_limit_; + double_data[6] = wconprod_line.THP_limit_; + double_data[7] = wconprod_line.VFP_table_number_; + double_data[8] = wconprod_line.artif_lift_quantity_; + readDefaultedVectorData(is, double_data, 9); + wconprod_line.oil_max_rate_ = double_data[0]; + wconprod_line.water_max_rate_ = double_data[1]; + wconprod_line.gas_max_rate_ = double_data[2]; + wconprod_line.liquid_max_rate_ = double_data[3]; + wconprod_line.fluid_volume_max_rate_ = double_data[4]; + wconprod_line.BHP_limit_ = double_data[5]; + wconprod_line.THP_limit_ = double_data[6]; + wconprod_line.VFP_table_number_ = (int)double_data[7]; + wconprod_line.artif_lift_quantity_ = double_data[8]; + wconprod.push_back(wconprod_line); + } + } + + virtual void write(std::ostream& os) const + { + os << name() << std::endl; + for (int i=0; i<(int) wconprod.size(); ++i) { + os << wconprod[i].well_ << " " + << wconprod[i].open_shut_flag_ << " " + << wconprod[i].control_mode_ << " " + << wconprod[i].oil_max_rate_ << " " + << wconprod[i].water_max_rate_ << " " + << wconprod[i].gas_max_rate_ << " " + << wconprod[i].liquid_max_rate_ << " " + << wconprod[i].fluid_volume_max_rate_ << " " + << wconprod[i].BHP_limit_ << " " + << wconprod[i].THP_limit_ << " " + << wconprod[i].VFP_table_number_ << " " + << wconprod[i].artif_lift_quantity_ + << std::endl; + } + os << std::endl; + } + + virtual void convertToSI(const EclipseUnits& units) + { + double lrat = units.liqvol_s / units.time; + double grat = units.gasvol_s / units.time; + double resv = units.liqvol_r / units.time; + for (int i=0; i<(int) wconprod.size(); ++i) { + wconprod[i].oil_max_rate_ *= lrat; + wconprod[i].water_max_rate_ *= lrat; + wconprod[i].gas_max_rate_ *= grat; + wconprod[i].liquid_max_rate_ *= lrat; + wconprod[i].fluid_volume_max_rate_ *= resv; + wconprod[i].BHP_limit_ *= units.pressure; + wconprod[i].THP_limit_ *= units.pressure; + } + } +}; + + +/// Class holding a data line of keyword WELTARG +struct WeltargLine +{ + std::string well_; // Well name or well name root + std::string control_change_; // Definition of the control or constraint + // quantity to be changed + double new_value_; // New value of this quantity + + WeltargLine() + { + } +}; + +/// Class for keyword WELTARG +struct WELTARG : public SpecialBase +{ + std::vector weltarg; + + WELTARG() + { + } + + virtual ~WELTARG() + {} + + virtual std::string name() const {return std::string("WELTARG");} + + virtual void read(std::istream& is) + { + while(is) { + std::string name = readString(is); + if (name[0] == '/') { + is >> ignoreLine; + break; + } + while (name.find("--") == 0) { + // This line is a comment + is >> ignoreLine; + name = readString(is); + } + WeltargLine weltarg_line; + weltarg_line.well_ = name; + weltarg_line.control_change_ = readString(is); + is >> weltarg_line.new_value_; + ignoreSlashLine(is); + weltarg.push_back(weltarg_line); + } + } + + virtual void write(std::ostream& os) const + { + os << name() << std::endl; + for (int i=0; i<(int)weltarg.size(); ++i) { + os << weltarg[i].well_ << " " + << weltarg[i].control_change_ << " " + << weltarg[i].new_value_ << " " + << std::endl; + } + os << std::endl; + } + + virtual void convertToSI(const EclipseUnits& units) + { + double lrat = units.liqvol_s / units.time; + double grat = units.gasvol_s / units.time; + double resv = units.liqvol_r / units.time; + for (int i=0; i<(int) weltarg.size(); ++i) { + if (weltarg[i].control_change_[0] == 'O' || + weltarg[i].control_change_[0] == 'W' || + weltarg[i].control_change_[0] == 'L') { + weltarg[i].new_value_ *= lrat; + } else if (weltarg[i].control_change_[0] == 'G') { + weltarg[i].new_value_ *= grat; + } else if (weltarg[i].control_change_[0] == 'R') { + weltarg[i].new_value_ *= resv; + } else if (weltarg[i].control_change_[0] == 'B' || + weltarg[i].control_change_[0] == 'T') { + weltarg[i].new_value_ *= units.pressure; + } else { + THROW("WELTARG. Unknown control or constraint " + << weltarg[i].control_change_[0]); + } + } + } +}; + + +/// Class holding a data line of keyword EQUIL +struct EquilLine +{ + double datum_depth_; // Well name or well name root. + double datum_depth_pressure_; // Pressure at datum depth. + double water_oil_contact_depth_; // Depth of water oil contact. + double oil_water_cap_pressure_; // Oil-water capillary pressure at the + // water-oil contact or Gas-water capillary + // pressure at the gas-water contact contact. + double gas_oil_contact_depth_; // Depth of the gas-oil contact. + double gas_oil_cap_pressure_; // Gas-oil capillary pressure at the gas-oil + // contact. + int live_oil_table_index_; // Rs v Depth or Pb v Depth table index for + // under-saturated live oil. + int wet_gas_table_index_; // Rv v Depth or Pd v Depth table index for + // under-saturated wet gas. + int N_; // Integer defining the accuracy of the + // initial fluids in place calculation. + EquilLine() + { + } +}; + +/// Class for keyword EQUIL +struct EQUIL : public SpecialBase +{ + std::vector equil; + + EQUIL() + { + } + + virtual ~EQUIL() + {} + + virtual std::string name() const {return std::string("EQUIL");} + + virtual void read(std::istream& is) + { + // Note. This function assumes that NTEQUL = 1, and reads only one line. + int num_to_read = 9; + std::vector data(num_to_read,0); + int num_read = readDefaultedVectorData(is, data, num_to_read); + if (num_read == num_to_read) { + ignoreSlashLine(is); + } + + EquilLine equil_line; + equil_line.datum_depth_ = data[0]; + equil_line.datum_depth_pressure_ = data[1]; + equil_line.water_oil_contact_depth_ = data[2]; + equil_line.oil_water_cap_pressure_ = data[3]; + equil_line.gas_oil_contact_depth_ = data[4]; + equil_line.gas_oil_cap_pressure_ = data[5]; + equil_line.live_oil_table_index_ = int(data[6]); + equil_line.wet_gas_table_index_ = int(data[7]); + equil_line.N_ = int(data[8]); + equil.push_back(equil_line); + } + + virtual void write(std::ostream& os) const + { + os << name() << std::endl; + for (int i=0; i<(int)equil.size(); ++i) { + os << equil[i].datum_depth_ << " " + << equil[i].datum_depth_pressure_ << " " + << equil[i].water_oil_contact_depth_ << " " + << equil[i].oil_water_cap_pressure_ << " " + << equil[i].gas_oil_contact_depth_ << " " + << equil[i].gas_oil_cap_pressure_ << " " + << equil[i].live_oil_table_index_ << " " + << equil[i].wet_gas_table_index_ << " " + << equil[i].N_ << " " + + << std::endl; + } + os << std::endl; + } + + virtual void convertToSI(const EclipseUnits& units) + { + for (int i=0; i<(int)equil.size(); ++i) { + equil[i].datum_depth_ *= units.length; + equil[i].datum_depth_pressure_ *= units.pressure; + equil[i].water_oil_contact_depth_ *= units.length; + equil[i].oil_water_cap_pressure_ *= units.pressure; + equil[i].gas_oil_contact_depth_ *= units.length; + equil[i].gas_oil_cap_pressure_ *= units.pressure; + } + } +}; + +struct PVCDO : public SpecialBase +{ + std::vector > pvcdo_; + + virtual std::string name() const {return std::string("PVCDO");} + + virtual void read(std::istream& is) + { + while (!is.eof()) { + std::vector pvcdo; + readVectorData(is, pvcdo); + if (pvcdo.size() == 4) { + pvcdo.push_back(0.0); + } + pvcdo_.push_back(pvcdo); + + int action = next_action(is); // 0:continue 1:return 2:throw + if (action == 1) { + return; // Alphabetic char. Read next keyword. + } else if (action == 2) { + THROW("Error reading PVCDO. Next character is " + << (char)is.peek()); + } + } + } + + virtual void write(std::ostream& os) const + { + os << name() << '\n'; + for (int i=0; i<(int)pvcdo_.size(); ++i) { + os << pvcdo_[i][0] << " " << pvcdo_[i][1] << " " << pvcdo_[i][2] + << " " << pvcdo_[i][3] << " " << pvcdo_[i][4] << '\n'; + } + os << '\n'; + } + + virtual void convertToSI(const EclipseUnits& units) + { + double volfac = units.liqvol_r/units.liqvol_s; + for (int i=0; i<(int)pvcdo_.size(); ++i) { + pvcdo_[i][0] *= units.pressure; + pvcdo_[i][1] *= volfac; + pvcdo_[i][2] *= units.compressibility; + pvcdo_[i][3] *= units.viscosity; + pvcdo_[i][4] *= units.compressibility; + } + } +}; + +struct MultRec : public SpecialBase +{ + virtual void read(std::istream& is) + { +#ifdef VERBOSE + std::cout << "(dummy implementation)" << std::endl; +#endif + const std::ctype& ct = std::use_facet< std::ctype >(std::locale::classic()); + is >> ignoreSlashLine; + while (!is.eof()) { + is >> ignoreWhitespace; + std::streampos pos = is.tellg(); + char c; + is.get(c); + if (is.eof()) { + return; + } + if (ct.is(std::ctype_base::alpha, c)) { + std::string name; // Unquoted name or new keyword? + std::getline(is, name); + if (name.rfind('/') != std::string::npos) { + continue; // Unquoted name + } else { + is.seekg(pos); + break; // Read next keyword + } + } else if (ct.is(std::ctype_base::digit, c) || c== '.') { + is >> ignoreSlashLine; // Decimal digit. Ignore data. + continue; + } else if (c== '\'') { + is >> ignoreSlashLine; // Quote. Ignore data. + continue; + } else if(c == '-' && is.peek() == int('-')) { + is >> ignoreLine; // This line is a comment + continue; + } else if (c == '/' ) { + is >> ignoreLine; // This line is a null record. + continue; // (No data before slash) + } else { + is.putback(c); + std::string temp; + is >> temp; + std::cout << "READ ERROR! Next word is " << temp << std::endl; + } + } + } + + virtual void convertToSI(const EclipseUnits&) + {} + +}; + +// The following fields only have a dummy implementation +// that allows us to ignore them. +struct SWFN : public MultRec {}; +struct SOF2 : public MultRec {}; +struct TUNING : public MultRec {}; + + +} // End of namespace Dune + +#endif // OPENRS_SPECIALECLIPSEFIELDS_HEADER + diff --git a/opm/core/utility/Average.hpp b/opm/core/utility/Average.hpp new file mode 100644 index 00000000..be2321d5 --- /dev/null +++ b/opm/core/utility/Average.hpp @@ -0,0 +1,98 @@ +//=========================================================================== +// +// File: Average.hpp<2> +// +// Created: Wed Jul 1 12:25:57 2009 +// +// Author(s): Atgeirr F Rasmussen +// Bård Skaflestad +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* + Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. + Copyright 2009, 2010 Statoil ASA. + + This file is part of The Open Reservoir Simulator Project (OpenRS). + + OpenRS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenRS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenRS. If not, see . +*/ + +#ifndef OPENRS_AVERAGE_HEADER +#define OPENRS_AVERAGE_HEADER + + +#include +#include +#include + +namespace Dune { + namespace utils { + + + + /// Computes the arithmetic average: + /// a_A(t_1, t_2) = (t_1 + t_2)/2. + template + Tresult arithmeticAverage(const T& t1, const T& t2) + { + // To avoid some user errors, we disallow taking averages of + // integral values. + BOOST_STATIC_ASSERT(std::tr1::is_integral::value == false); + Tresult retval(t1); + retval += t2; + retval *= 0.5; + return retval; + } + + + + /// Computes the geometric average: + /// a_G(t_1, t_2) = \sqrt{t_1 t_2}. + /// Since we use std::sqrt(), this function will only + /// compile when T is convertible to and from double. + template + T geometricAverage(const T& t1, const T& t2) + { + // To avoid some user errors, we disallow taking averages of + // integral values. + BOOST_STATIC_ASSERT(std::tr1::is_integral::value == false); + return std::sqrt(t1*t2); + } + + + + /// Computes the harmonic average: + /// a_H(t_1, t_2) = \frac{2}{1/t_1 + 1/t_2} = \frac{2 t_1 t_2}{t_1 + t_2}. + template + T harmonicAverage(const T& t1, const T& t2) + { + // To avoid some user errors, we disallow taking averages of + // integral values. + BOOST_STATIC_ASSERT(std::tr1::is_integral::value == false); + return (2*t1*t2)/(t1 + t2); + } + + + + } // namespace utils +} // namespace Dune + + + +#endif // OPENRS_AVERAGE_HEADER diff --git a/opm/core/utility/ErrorMacros.hpp b/opm/core/utility/ErrorMacros.hpp new file mode 100644 index 00000000..10992251 --- /dev/null +++ b/opm/core/utility/ErrorMacros.hpp @@ -0,0 +1,107 @@ +//=========================================================================== +// +// File: ErrorMacros.hpp +// +// Created: Tue May 14 12:22:16 2002 +// +// Author(s): Atgeirr F Rasmussen +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* +Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. +Copyright 2009, 2010 Statoil ASA. + +This file is part of The Open Reservoir Simulator Project (OpenRS). + +OpenRS is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenRS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OpenRS. If not, see . +*/ + +#ifndef OPENRS_ERRORMACROS_HEADER +#define OPENRS_ERRORMACROS_HEADER + + +/// Error macros. In order to use some of them, you must also +/// include or , so they are included by +/// this file. The compile defines NDEBUG and NVERBOSE control +/// the behaviour of these macros. + +#ifndef NVERBOSE + #include +#endif +#include + +/// Usage: REPORT; +/// Usage: MESSAGE("Message string."); +#ifdef NVERBOSE // Not verbose mode +# ifndef REPORT +# define REPORT +# endif +# ifndef MESSAGE +# define MESSAGE(x) +# endif +# ifndef MESSAGE_IF +# define MESSAGE_IF(cond, m) +# endif +#else // Verbose mode +# ifndef REPORT +# define REPORT std::cerr << "\nIn file " << __FILE__ << ", line " << __LINE__ << std::endl +# endif +# ifndef MESSAGE +# define MESSAGE(x) std::cerr << "\nIn file " << __FILE__ << ", line " << __LINE__ << ": " << x << std::endl +# endif +# ifndef MESSAGE_IF +# define MESSAGE_IF(cond, m) do {if(cond) MESSAGE(m);} while(0) +# endif +#endif + + +/// Usage: THROW("Error message string."); +#ifndef THROW +# define THROW(x) do { MESSAGE(x); throw std::exception(); } while(0) +#endif + +#define ALWAYS_ERROR_IF(condition, message) do {if(condition){ THROW(message);}} while(0) + +/// Usage: ASSERT(condition) +/// Usage: ASSERT2(condition, "Error message string.") +/// Usage: DEBUG_ERROR_IF(condition, "Error message string."); +#ifdef NDEBUG // Not in debug mode +# ifndef ASSERT +# define ASSERT(x) +# endif +# ifndef ASSERT2 +# define ASSERT2(cond, x) +# endif +# ifndef DEBUG_ERROR_IF +# define DEBUG_ERROR_IF(cond, x) +# endif +#else // Debug mode +# ifndef ASSERT +# define ASSERT(cond) if (!(cond)) THROW("Assertion \'" #cond "\' failed.") +# endif +# ifndef ASSERT2 +# define ASSERT2(cond, x) do { if (!(cond)) THROW(x);} while(0) +# endif +# ifndef DEBUG_ERROR_IF +# define DEBUG_ERROR_IF(cond, x) do { if (cond) THROW(x); } while(0) +# endif +#endif + + +#endif // OPENRS_ERRORMACROS_HEADER diff --git a/opm/core/utility/Factory.hpp b/opm/core/utility/Factory.hpp new file mode 100644 index 00000000..8765a936 --- /dev/null +++ b/opm/core/utility/Factory.hpp @@ -0,0 +1,145 @@ +//=========================================================================== +// +// File: Factory.hpp +// +// Created: Mon Nov 6 10:00:43 2000 +// +// Author(s): Atgeirr F Rasmussen +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* + Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. + Copyright 2009, 2010 Statoil ASA. + + This file is part of The Open Reservoir Simulator Project (OpenRS). + + OpenRS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenRS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenRS. If not, see . +*/ + +#ifndef OPENRS_FACTORY_HEADER +#define OPENRS_FACTORY_HEADER + + +#include +#include + +namespace Dune +{ + + /** This is an object factory for creating objects of some type + * requested by the user, with a shared base class. The user + * need only interact with the factory through the static + * template member addCreator() and the static member function + * createObject(). + */ + template + class Factory + { + public: + /// The type of pointer returned by createObject(). + typedef std::tr1::shared_ptr ProductPtr; + + /// Creates a new object of the class associated with the given type string, + /// and returns a pointer to it. + /// \param type the type string of the class that the user wants to have + /// constructed. + /// \return (smart) pointer to the created object. + static ProductPtr createObject(const std::string& type) + { + return instance().doCreateObject(type); + } + + /// Add a creator to the Factory. + /// After the call, the user may obtain new objects of the Derived type by + /// calling createObject() with the given type string as an argument. + /// \tparam Derived the class we want to add a creator for, must inherit + /// the class template parameter Base. + /// \param type the type string with which we want the Factory to associate + /// the class Derived. + template + static void addCreator(const std::string& type) + { + instance().doAddCreator(type); + } + + private: + // The method that implements the singleton pattern, + // using the Meyers singleton technique. + static Factory& instance() + { + static Factory singleton; + return singleton; + } + + // Private constructor, to keep users from creating a Factory. + Factory() + { + } + + // Abstract base class for Creators. + class Creator + { + public: + virtual ProductPtr create() = 0; + virtual ~Creator() {} + }; + + /// This is the concrete Creator subclass for generating Derived objects. + template + class ConcreteCreator : public Creator + { + public: + virtual ProductPtr create() + { + return ProductPtr(new Derived); + } + }; + + typedef std::tr1::shared_ptr CreatorPtr; + typedef std::map CreatorMap; + // This map contains the whole factory, i.e. all the Creators. + CreatorMap string_to_creator_; + + // Actually creates the product object. + ProductPtr doCreateObject(const std::string& type) + { + typename CreatorMap::iterator it; + it = string_to_creator_.find(type); + if (it == string_to_creator_.end()) { + THROW("Creator type " << type + << " is not registered in the factory."); + } + return it->second->create(); + } + + // Actually adds the creator. + template + void doAddCreator(const std::string& type) + { + CreatorPtr c(new ConcreteCreator); + string_to_creator_[type] = c; + } + }; + + +} // namespace Dune + +#endif // OPENRS_FACTORY_HEADER + + diff --git a/opm/core/utility/MonotCubicInterpolator.cpp b/opm/core/utility/MonotCubicInterpolator.cpp new file mode 100644 index 00000000..332ead06 --- /dev/null +++ b/opm/core/utility/MonotCubicInterpolator.cpp @@ -0,0 +1,726 @@ +/* + MonotCubicInterpolator + Copyright (C) 2006 Statoil ASA + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** + @file MonotCubicInterpolator.C + @brief Represents one dimensional function f with single valued argument x + + Class to represent a one-dimensional function with single-valued + argument. Cubic interpolation, preserving the monotonicity of the + discrete known function values + + @see MonotCubicInterpolator.h for further documentation. + +*/ + + +#include "MonotCubicInterpolator.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +/* + + SOME DISCUSSION ON DATA STORAGE: + + Internal data structure of points and values: + + vector(s): + - Easier coding + - Faster vector operations when setting up derivatives. + - sorting slightly more complex. + - insertion of further values bad. + + vector + - easy sorting + - code complexity almost as for map. + - insertion of additional values bad + + vector over struct datapoint { x, f, d} + - nice code + - not as sortable, insertion is cumbersome. + + ** This is used currently: ** + map one for (x,f) and one for (x,d) + - Naturally sorted on x-values (done by the map-construction) + - Slower to set up, awkward loop coding (?) + - easy to add more points. + - easier to just add code to linear interpolation code + - x-data is duplicated, but that memory waste is + unlikely to represent a serious issue. + + map > + - naturally couples x-value, f-value and d-value + - even more unreadable(??) code? + - higher skills needed by programmer. + + + MONOTONE CUBIC INTERPOLATION: + + It is a local algorithm. Adding one point only incur recomputation + of values in a neighbourhood of the point (in the interval getting + divided). + + NOTE: We do not currently make use of this local fact. Upon + insertion of a new data pair, everything is recomputed. Revisit + this when needed. + +*/ + + + + +MonotCubicInterpolator:: +MonotCubicInterpolator(const vector & x, const vector & f) { + if (x.size() != f.size()) { + throw("Unable to constuct MonotCubicInterpolator from vectors.") ; + } + + // Add the contents of the input vectors to our map of values. + vector::const_iterator posx, posf; + for (posx = x.begin(), posf = f.begin(); posx != x.end(); ++posx, ++posf) { + data[*posx] = *posf ; + } + + computeInternalFunctionData(); +} + + + +bool +MonotCubicInterpolator:: +read(const std::string & datafilename, int xColumn, int fColumn) +{ + data.clear() ; + ddata.clear() ; + + ifstream datafile_fs(datafilename.c_str()); + if (!datafile_fs) { + return false ; + } + + string linestring; + while (!datafile_fs.eof()) { + getline(datafile_fs, linestring); + + // Replace commas with space: + string::size_type pos = 0; + while ( (pos = linestring.find(",", pos)) != string::npos ) { + // cout << "Found comma at position " << pos << endl; + linestring.replace(pos, 1, " "); + pos++; + } + + stringstream strs(linestring); + int columnindex = 0; + std::vector value; + if (linestring.size() > 0 && linestring.at(0) != '#') { + while (!(strs.rdstate() & std::ios::failbit)) { + double tmp; + strs >> tmp; + value.push_back(tmp); + columnindex++; + } + } + if (columnindex >= (max(xColumn, fColumn))) { + data[value[xColumn-1]] = value[fColumn-1] ; + } + } + datafile_fs.close(); + + if (data.size() == 0) { + return false ; + } + + computeInternalFunctionData(); + return true ; +} + + +void +MonotCubicInterpolator:: +addPair(double newx, double newf) throw(const char*) { + if (std::isnan(newx) || std::isinf(newx) || std::isnan(newf) || std::isinf(newf)) { + throw("MonotCubicInterpolator: addPair() received inf/nan input."); + } + data[newx] = newf ; + + // In a critical application, we should only update the + // internal function data for the offended interval, + // not for all function values over again. + computeInternalFunctionData(); +} + + +double +MonotCubicInterpolator:: +evaluate(double x) const throw(const char*){ + + if (std::isnan(x) || std::isinf(x)) { + throw("MonotCubicInterpolator: evaluate() received inf/nan input."); + } + + // xf becomes the first (xdata,fdata) pair where xdata >= x + map::const_iterator xf_iterator = data.lower_bound(x); + + // First check if we must extrapolate: + if (xf_iterator == data.begin()) { + if (data.begin()->first == x) { // Just on the interval limit + return data.begin()->second; + } + else { + // Constant extrapolation (!!) + return data.begin()->second; + } + } + if (xf_iterator == data.end()) { + // Constant extrapolation (!!) + return data.rbegin()->second; + } + + + // Ok, we have x_min < x < x_max + + pair xf2 = *xf_iterator; + pair xf1 = *(--xf_iterator); + // we now have: xf2.first > x + + // Linear interpolation if derivative data is not available: + if (ddata.size() != data.size()) { + double finterp = xf1.second + + (xf2.second - xf1.second) / (xf2.first - xf1.first) + * (x - xf1.first); + return finterp; + } + else { // Do Cubic Hermite spline + double t = (x - xf1.first)/(xf2.first - xf1.first); // t \in [0,1] + double h = xf2.first - xf1.first; + double finterp + = xf1.second * H00(t) + + ddata[xf1.first] * H10(t) * h + + xf2.second * H01(t) + + ddata[xf2.first] * H11(t) * h ; + return finterp; + } + +} + + +// double +// MonotCubicInterpolator:: +// evaluate(double x, double& errorestimate_output) { +// cout << "Error: errorestimate not implemented" << endl; +// throw("error estimate not implemented"); +// return x; +// } + +vector +MonotCubicInterpolator:: +get_xVector() const +{ + map::const_iterator xf_iterator = data.begin(); + + vector outputvector; + outputvector.reserve(data.size()); + for (xf_iterator = data.begin(); xf_iterator != data.end(); ++xf_iterator) { + outputvector.push_back(xf_iterator->first); + } + return outputvector; +} + + +vector +MonotCubicInterpolator:: +get_fVector() const +{ + + map::const_iterator xf_iterator = data.begin(); + + vector outputvector; + outputvector.reserve(data.size()); + for (xf_iterator = data.begin(); xf_iterator != data.end(); ++xf_iterator) { + outputvector.push_back(xf_iterator->second); + } + return outputvector; +} + + + +string +MonotCubicInterpolator:: +toString() const +{ + const int precision = 20; + std::string dataString; + std::stringstream dataStringStream; + for (map::const_iterator it = data.begin(); + it != data.end(); ++it) { + dataStringStream << setprecision(precision) << it->first; + dataStringStream << '\t'; + dataStringStream << setprecision(precision) << it->second; + dataStringStream << '\n'; + } + dataStringStream << "Derivative values:" << endl; + for (map::const_iterator it = ddata.begin(); + it != ddata.end(); ++it) { + dataStringStream << setprecision(precision) << it->first; + dataStringStream << '\t'; + dataStringStream << setprecision(precision) << it->second; + dataStringStream << '\n'; + } + + return dataStringStream.str(); + +} + + +pair +MonotCubicInterpolator:: +getMissingX() const throw(const char*) +{ + if( data.size() < 2) { + throw("MonotCubicInterpolator::getMissingX() only one datapoint."); + } + + // Search for biggest difference value in function-datavalues: + + map::const_iterator maxfDiffPair_iterator = data.begin();; + double maxfDiffValue = 0; + + map::const_iterator xf_iterator; + map::const_iterator xf_next_iterator; + + for (xf_iterator = data.begin(), xf_next_iterator = ++(data.begin()); + xf_next_iterator != data.end(); + ++xf_iterator, ++xf_next_iterator) { + double absfDiff = fabs((double)(*xf_next_iterator).second + - (double)(*xf_iterator).second); + if (absfDiff > maxfDiffValue) { + maxfDiffPair_iterator = xf_iterator; + maxfDiffValue = absfDiff; + } + } + + double newXvalue = ((*maxfDiffPair_iterator).first + ((*(++maxfDiffPair_iterator)).first))/2; + return make_pair(newXvalue, maxfDiffValue); + +} + + + +pair +MonotCubicInterpolator:: +getMaximumF() const throw(const char*) { + if (data.size() <= 1) { + throw ("MonotCubicInterpolator::getMaximumF() empty data.") ; + } + if (strictlyIncreasing) + return *data.rbegin(); + else if (strictlyDecreasing) + return *data.begin(); + else { + pair maxf = *data.rbegin() ; + map::const_iterator xf_iterator; + for (xf_iterator = data.begin() ; xf_iterator != data.end(); ++xf_iterator) { + if (xf_iterator->second > maxf.second) { + maxf = *xf_iterator ; + } ; + } + return maxf ; + } +} + + +pair +MonotCubicInterpolator:: +getMinimumF() const throw(const char*) { + if (data.size() <= 1) { + throw ("MonotCubicInterpolator::getMinimumF() empty data.") ; + } + if (strictlyIncreasing) + return *data.begin(); + else if (strictlyDecreasing) { + return *data.rbegin(); + } + else { + pair minf = *data.rbegin() ; + map::const_iterator xf_iterator; + for (xf_iterator = data.begin() ; xf_iterator != data.end(); ++xf_iterator) { + if (xf_iterator->second < minf.second) { + minf = *xf_iterator ; + } ; + } + return minf ; + } +} + + +void +MonotCubicInterpolator:: +computeInternalFunctionData() const { + + /* The contents of this function is meaningless if there is only one datapoint */ + if (data.size() <= 1) { + return; + } + + /* We do not check the caching flag if we are instructed + to do this computation */ + + /* We compute monotoneness and directions by assuming + monotoneness, and setting to false if the function is not for + some value */ + + map::const_iterator xf_iterator; + map::const_iterator xf_next_iterator; + + + strictlyMonotone = true; // We assume this is true, and will set to false if not + monotone = true; + strictlyDecreasing = true; + decreasing = true; + strictlyIncreasing = true; + increasing = true; + + // Increasing or decreasing?? + xf_iterator = data.begin(); + xf_next_iterator = ++(data.begin()); + /* Cater for non-strictness, search for direction for monotoneness */ + while (xf_next_iterator != data.end() && + xf_iterator->second == xf_next_iterator->second) { + /* Ok, equal values, this is not strict. */ + strictlyMonotone = false; + strictlyIncreasing = false; + strictlyDecreasing = false; + + ++xf_iterator; + ++xf_next_iterator; + } + + + if (xf_next_iterator != data.end()) { + + if ( xf_iterator->second > xf_next_iterator->second) { + // Ok, decreasing, check monotoneness: + strictlyDecreasing = true;// if strictlyMonotone == false, this one should not be trusted anyway + decreasing = true; + strictlyIncreasing = false; + increasing = false; + while(++xf_iterator, ++xf_next_iterator != data.end()) { + if ((*xf_iterator).second < (*xf_next_iterator).second) { + monotone = false; + strictlyMonotone = false; + strictlyDecreasing = false; // meaningless now + break; // out of while loop + } + if ((*xf_iterator).second <= (*xf_next_iterator).second) { + strictlyMonotone = false; + strictlyDecreasing = false; // meaningless now + } + } + } + else if (xf_iterator->second < xf_next_iterator->second) { + // Ok, assume increasing, check monotoneness: + strictlyDecreasing = false; + strictlyIncreasing = true; + decreasing = false; + increasing = true; + while(++xf_iterator, ++xf_next_iterator != data.end()) { + if ((*xf_iterator).second > (*xf_next_iterator).second) { + monotone = false; + strictlyMonotone = false; + strictlyIncreasing = false; // meaningless now + break; // out of while loop + } + if ((*xf_iterator).second >= (*xf_next_iterator).second) { + strictlyMonotone = false; + strictlyIncreasing = false; // meaningless now + } + } + } + else { + // first two values must be equal if we get + // here, but that should have been taken care + // of by the while loop above. + throw("Programming logic error.") ; + } + + } + computeSimpleDerivatives(); + + + // If our input data is monotone, we can do monotone cubic + // interpolation, so adjust the derivatives if so. + // + // If input data is not monotone, we should not touch + // the derivatives, as this code should reduce to a + // standard cubic interpolation algorithm. + if (monotone) { + adjustDerivativesForMonotoneness(); + } + + strictlyMonotoneCached = true; + monotoneCached = true; +} + +// Checks if the function curve is flat (zero derivative) at the +// endpoints, chop off endpoint data points if that is the case. +// +// The notion of "flat" is determined by the input parameter "epsilon" +// Values whose difference are less than epsilon are regarded as equal. +// +// This is implemented to be able to obtain a strictly monotone +// curve from a data set that is strictly monotone except at the +// endpoints. +// +// Example: +// The data points +// (1,3), (2,3), (3,4), (4,5), (5,5), (6,5) +// will become +// (2,3), (3,4), (4,5) +// +// Assumes at least 3 datapoints. If less than three, this function is a noop. +void +MonotCubicInterpolator:: +chopFlatEndpoints(const double epsilon) { + + if (getSize() < 3) { + return; + } + + map::iterator xf_iterator; + map::iterator xf_next_iterator; + + // Clear flags: + strictlyMonotoneCached = false; + monotoneCached = false; + + // Chop left end: + xf_iterator = data.begin(); + xf_next_iterator = ++(data.begin()); + // Erase data points that are similar to its right value from the left end. + while ((xf_next_iterator != data.end()) && + (fabs(xf_iterator->second - xf_next_iterator->second) < epsilon )) { + xf_next_iterator++; + data.erase(xf_iterator); + xf_iterator++; + } + + xf_iterator = data.end(); + xf_iterator--; // (data.end() points beyond the last element) + xf_next_iterator = xf_iterator; + xf_next_iterator--; + // Erase data points that are similar to its left value from the right end. + while ((xf_next_iterator != data.begin()) && + (fabs(xf_iterator->second - xf_next_iterator->second) < epsilon )) { + xf_next_iterator--; + data.erase(xf_iterator); + xf_iterator--; + } + + // Finished chopping, so recompute function data: + computeInternalFunctionData(); +} + + +// +// If function is monotone, but not strictly monotone, +// this function will remove datapoints from intervals +// with zero derivative so that the curves become +// strictly monotone. +// +// Example +// The data points +// (1,2), (2,3), (3,4), (4,4), (5,5), (6,6) +// will become +// (1,2), (2,3), (3,4), (5,5), (6,6) +// +// Assumes at least two datapoints, if one or zero datapoint, this is a noop. +// +// +void +MonotCubicInterpolator:: +shrinkFlatAreas(const double epsilon) { + + if (getSize() < 2) { + return; + } + + map::iterator xf_iterator; + map::iterator xf_next_iterator; + + + // Nothing to do if we already are strictly monotone + if (isStrictlyMonotone()) { + return; + } + + // Refuse to change a curve that is not monotone. + if (!isMonotone()) { + return; + } + + // Clear flags, they are not to be trusted after we modify the + // data + strictlyMonotoneCached = false; + monotoneCached = false; + + // Iterate through data values, if two data pairs + // have equal values, delete one of the data pair. + // Do not trust the source code on which data point is being + // removed (x-values of equal y-points might be averaged in the future) + xf_iterator = data.begin(); + xf_next_iterator = ++(data.begin()); + + while (xf_next_iterator != data.end()) { + //cout << xf_iterator->first << "," << xf_iterator->second << " " << xf_next_iterator->first << "," << xf_next_iterator->second << "\n"; + if (fabs(xf_iterator->second - xf_next_iterator->second) < epsilon ) { + //cout << "erasing data pair" << xf_next_iterator->first << " " << xf_next_iterator->second << "\n"; + map ::iterator xf_tobedeleted_iterator = xf_next_iterator; + xf_next_iterator++; + data.erase(xf_tobedeleted_iterator); + } + else { + xf_iterator++; + xf_next_iterator++; + } + } + +} + + +void +MonotCubicInterpolator:: +computeSimpleDerivatives() const { + + ddata.clear(); + + // Do endpoints first: + map::const_iterator xf_prev_iterator; + map::const_iterator xf_iterator; + map::const_iterator xf_next_iterator; + double diff; + + // Leftmost interval: + xf_iterator = data.begin(); + xf_next_iterator = ++(data.begin()); + diff = + (xf_next_iterator->second - xf_iterator->second) / + (xf_next_iterator->first - xf_iterator->first); + ddata[xf_iterator->first] = diff ; + + // Rightmost interval: + xf_iterator = --(--(data.end())); + xf_next_iterator = --(data.end()); + diff = + (xf_next_iterator->second - xf_iterator->second) / + (xf_next_iterator->first - xf_iterator->first); + ddata[xf_next_iterator->first] = diff ; + + // If we have more than two intervals, loop over internal points: + if (data.size() > 2) { + + map::const_iterator intpoint; + for (intpoint = ++data.begin(); intpoint != --data.end(); ++intpoint) { + /* + diff = (f2 - f1)/(x2-x1)/w + (f3-f1)/(x3-x2)/2 + + average of the forward and backward difference. + Weights are equal, should we weigh with h_i? + */ + + map::const_iterator lastpoint = intpoint; --lastpoint; + map::const_iterator nextpoint = intpoint; ++nextpoint; + + diff = (nextpoint->second - intpoint->second)/ + (2*(nextpoint->first - intpoint->first)) + + + (intpoint->second - lastpoint->second) / + (2*(intpoint->first - lastpoint->first)); + + ddata[intpoint->first] = diff ; + } + } +} + + + +void +MonotCubicInterpolator:: +adjustDerivativesForMonotoneness() const { + map::const_iterator point, dpoint; + + /* Loop over all intervals, ie. loop over all points and look + at the interval to the right of the point */ + for (point = data.begin(), dpoint = ddata.begin(); + point != --data.end(); + ++point, ++dpoint) { + map::const_iterator nextpoint, nextdpoint; + nextpoint = point; ++nextpoint; + nextdpoint = dpoint; ++nextdpoint; + + double delta = + (nextpoint->second - point->second) / + (nextpoint->first - point->first); + if (fabs(delta) < 1e-14) { + ddata[point->first] = 0.0; + ddata[nextpoint->first] = 0.0; + } else { + double alpha = ddata[point->first] / delta; + double beta = ddata[nextpoint->first] / delta; + + if (! isMonotoneCoeff(alpha, beta)) { + double tau = 3/sqrt(alpha*alpha + beta*beta); + + ddata[point->first] = tau*alpha*delta; + ddata[nextpoint->first] = tau*beta*delta; + } + } + + + } + + +} + + + + +void +MonotCubicInterpolator:: +scaleData(double factor) { + map::iterator it , itd ; + if (data.size() == ddata.size()) { + for (it = data.begin() , itd = ddata.begin() ; it != data.end() ; ++it , ++itd) { + it->second *= factor ; + itd->second *= factor ; + } ; + } else { + for (it = data.begin() ; it != data.end() ; ++it ) { + it->second *= factor ; + } + } +} diff --git a/opm/core/utility/MonotCubicInterpolator.hpp b/opm/core/utility/MonotCubicInterpolator.hpp new file mode 100644 index 00000000..7e1c6710 --- /dev/null +++ b/opm/core/utility/MonotCubicInterpolator.hpp @@ -0,0 +1,578 @@ +/* -*-C++-*- */ + +#ifndef _MONOTCUBICINTERPOLATOR_H +#define _MONOTCUBICINTERPOLATOR_H + +#include +#include +#include + +/* + MonotCubicInterpolator + Copyright (C) 2006 Statoil ASA + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** + Class to represent a one-dimensional function f with single-valued + argument x. The function is represented by a table of function + values. Interpolation between table values is cubic and monotonicity + preserving if input values are monotonous. + + Outside x_min and x_max, the class will extrapolate using the + constant f(x_min) or f(x_max). + + Extra functionality: + - Can return (x_1+x_2)/2 where x_1 and x_2 are such that + abs(f(x_1) - f(x_2)) is maximized. This is used to determine where + one should calculate a new value for increased accuracy in the + current function + + Monotonicity preserving cubic interpolation algorithm is taken + from Fritsch and Carlson, "Monotone piecewise cubic interpolation", + SIAM J. Numer. Anal. 17, 238--246, no. 2, + + $Id$ + + Algorithm also described here: + http://en.wikipedia.org/wiki/Monotone_cubic_interpolation + + + @author HÃ¥vard Berland , December 2006 + @brief Represents one dimensional function f with single valued argument x that can be interpolated using monotone cubic interpolation + +*/ + +class MonotCubicInterpolator { + public: + + /** + @param datafilename A datafile with the x values and the corresponding f(x) values + + Accepts a filename as input and parses this file for + two-column floating point data, interpreting the data as + representing function values x and f(x). + + Ignores all lines not conforming to \\\\\\ + */ + MonotCubicInterpolator(const std::string & datafilename) throw (const char*) + { + if (!read(datafilename)) { + throw("Unable to constuct MonotCubicInterpolator from file.") ; + } ; + } ; + + + /** + @param datafilename A datafile with the x values and the corresponding f(x) values + + Accepts a filename as input and parses this file for + two-column floating point data, interpreting the data as + representing function values x and f(x). + + Ignores all lines not conforming to \\\\\\ + + All commas in the file will be treated as spaces when parsing. + + */ + + MonotCubicInterpolator(const char* datafilename) throw (const char*) + { + if (!read(std::string(datafilename))) { + throw("Unable to constuct MonotCubicInterpolator from file.") ; + } ; + } ; + + + /** + @param datafilename data file + @param XColumn x values + @param fColumn f values + + Accepts a filename as input, and parses the chosen columns in + that file. + */ + MonotCubicInterpolator(const char* datafilename, int xColumn, int fColumn) throw (const char*) + { + if (!read(std::string(datafilename),xColumn,fColumn)) { + throw("Unable to constuct MonotCubicInterpolator from file.") ; + } ; + } ; + + /** + @param datafilename data file + @param XColumn x values + @param fColumn f values + + Accepts a filename as input, and parses the chosen columns in + that file. + */ + MonotCubicInterpolator(const std::string & datafilename, int xColumn, int fColumn) throw (const char*) + { + if (!read(datafilename,xColumn,fColumn)) { + throw("Unable to constuct MonotCubicInterpolator from file.") ; + } ; + } ; + + /** + @param x vector of x values + @param f vector of corresponding f values + + Accepts two equal-length vectors as input for constructing + the interpolation object. First vector is the x-values, the + second vector is the function values + */ + MonotCubicInterpolator(const std::vector & x , + const std::vector & f); + + /** + No input, an empty function object is created. + + This object must be treated with care until + populated. + */ + MonotCubicInterpolator() {;} ; + + + + /** + @param datafilename A datafile with the x values and the corresponding f(x) values + + Accepts a filename as input and parses this file for + two-column floating point data, interpreting the data as + representing function values x and f(x). + + returns true on success + + All commas in file will be treated as spaces when parsing + + Ignores all lines not conforming to \\\\\\ + */ + bool read(const std::string & datafilename) { + return read(datafilename,1,2) ; + } ; + + /** + @param datafilename data file + @param XColumn x values + @param fColumn f values + + Accepts a filename as input, and parses the chosen columns in + that file. + */ + bool read(const std::string & datafilename, int xColumn, int fColumn) ; + + + + /** + @param x x value + + Returns f(x) for given x (input). Interpolates (monotone cubic + or linearly) if necessary. + + Extrapolates using the constants f(x_min) or f(x_max) if + input x is outside (x_min, x_max) + + @return f(x) for a given x + */ + double operator () (double x) const { return evaluate(x) ; } ; + + /** + @param x x value + + Returns f(x) for given x (input). Interpolates (monotone cubic + or linearly) if necessary. + + Extrapolates using the constants f(x_min) or f(x_max) if + input x is outside (x_min, x_max) + + @return f(x) for a given x + */ + double evaluate(double x) const throw(const char*); + + /** + @param x x value + @param errorestimate_output + + Returns f(x) and an error estimate for given x (input). + + Interpolates (linearly) if necessary. + + Throws an exception if extrapolation would be necessary for + evaluation. We do not want to do extrapolation (yet). + + The error estimate for x1 < x < x2 is + (x2 - x1)^2/8 * f''(x) where f''(x) is evaluated using + the stencil (1 -2 1) using either (x0, x1, x2) or (x1, x2, x3); + + Throws an exception if the table contains only two x-values. + + NOT IMPLEMENTED YET! + */ + double evaluate(double x, double & errorestimate_output ) const ; + + /** + Minimum x-value, returns both x and f in a pair. + + @return minimum x value + @return f(minimum x value) + */ + std::pair getMinimumX() const { + // Easy since the data is sorted on x: + return *data.begin(); + } + + /** + Maximum x-value, returns both x and f in a pair. + + @return maximum x value + @return f(maximum x value) + */ + std::pair getMaximumX() const { + // Easy since the data is sorted on x: + return *data.rbegin(); + } + + /** + Maximum f-value, returns both x and f in a pair. + + @return x value corresponding to maximum f value + @return maximum f value + */ + std::pair getMaximumF() const throw(const char*) ; + + /** + Minimum f-value, returns both x and f in a pair + + @return x value corresponding to minimal f value + @return minimum f value + */ + std::pair getMinimumF() const throw(const char*) ; + + + /** + Provide a copy of the x-data as a vector + + Unspecified order, but corresponds to get_fVector. + + @return x values as a vector + */ + std::vector get_xVector() const ; + + /** + Provide a copy of tghe function data as a vector + + Unspecified order, but corresponds to get_xVector + + @return f values as a vector + + */ + std::vector get_fVector() const ; + + /** + @param factor Scaling constant + + Scale all the function value data by a constant + */ + void scaleData(double factor); + + /** + Determines if the current function-value-data is strictly + monotone. This is a utility function for outsiders if they want + to invert the data for example. + + @return True if f(x) is strictly monotone, else False + */ + bool isStrictlyMonotone() { + + /* Use cached value if it can be trusted */ + if (strictlyMonotoneCached) { + return strictlyMonotone; + } + else { + computeInternalFunctionData(); + return strictlyMonotone; + } + } + + /** + Determines if the current function-value-data is monotone. + + @return True if f(x) is monotone, else False + */ + bool isMonotone() const { + if (monotoneCached) { + return monotone; + } + else { + computeInternalFunctionData(); + return monotone; + } + } + /** + Determines if the current function-value-data is strictly + increasing. This is a utility function for outsiders if they want + to invert the data for example. + + @return True if f(x) is strictly increasing, else False + */ + bool isStrictlyIncreasing() { + + /* Use cached value if it can be trusted */ + if (strictlyMonotoneCached) { + return (strictlyMonotone && strictlyIncreasing); + } + else { + computeInternalFunctionData(); + return (strictlyMonotone && strictlyIncreasing); + } + } + + /** + Determines if the current function-value-data is monotone and increasing. + + @return True if f(x) is monotone and increasing, else False + */ + bool isMonotoneIncreasing() const { + if (monotoneCached) { + return (monotone && increasing); + } + else { + computeInternalFunctionData(); + return (monotone && increasing); + } + } + /** + Determines if the current function-value-data is strictly + decreasing. This is a utility function for outsiders if they want + to invert the data for example. + + @return True if f(x) is strictly decreasing, else False + */ + bool isStrictlyDecreasing() { + + /* Use cached value if it can be trusted */ + if (strictlyMonotoneCached) { + return (strictlyMonotone && strictlyDecreasing); + } + else { + computeInternalFunctionData(); + return (strictlyMonotone && strictlyDecreasing); + } + } + + /** + Determines if the current function-value-data is monotone and decreasing + + @return True if f(x) is monotone and decreasing, else False + */ + bool isMonotoneDecreasing() const { + if (monotoneCached) { + return (monotone && decreasing); + } + else { + computeInternalFunctionData(); + return (monotone && decreasing); + } + } + + + + /** + @param newx New x point + @param newf New f(x) point + + Adds a new datapoint to the function. + + This causes all the derivatives at all points of the functions + to be recomputed and then adjusted for monotone cubic + interpolation. If this function ever enters a critical part of + any code, the locality of the algorithm for monotone adjustment + must be exploited. + + */ + void addPair(double newx, double newf) throw(const char*); + + /** + Returns an x-value that is believed to yield the best + improvement in global accuracy for the interpolation if + computed. + + Searches for the largest jump in f-values, and returns a x + value being the average of the two x-values representing the + f-value-jump. + + @return New x value beleived to yield the best improvement in global accuracy + @return Maximal difference + */ + std::pair getMissingX() const throw(const char*) ; + + /** + Constructs a string containing the data in a table + + @return a string containing the data in a table + */ + std::string toString() const; + + /** + @return Number of datapoint pairs in this object + */ + int getSize() const { + return data.size(); + } + + /** + Checks if the function curve is flat at the endpoints, chop off + endpoint data points if that is the case. + + The notion of "flat" is determined by the input parameter "epsilon" + Values whose difference are less than epsilon are regarded as equal. + + This is implemented to be able to obtain a strictly monotone + curve from a data set that is strictly monotone except at the + endpoints. + + Example: + The data points + (1,3), (2,3), (3,4), (4,5), (5,5), (6,5) + will become + (2,3), (3,4), (4,5) + + Assumes at least 3 datapoints. If less than three, this function is a noop. + */ + void chopFlatEndpoints(const double); + + /** + Wrapper function for chopFlatEndpoints(const double) + providing a default epsilon parameter + */ + void chopFlatEndpoints() { + chopFlatEndpoints(1e-14); + } + + /** + If function is monotone, but not strictly monotone, + this function will remove datapoints from intervals + with zero derivative so that the curve become + strictly monotone. + + Example + The data points + (1,2), (2,3), (3,4), (4,4), (5,5), (6,6) + will become + (1,2), (2,3), (3,4), (5,5), (6,6) + + Assumes at least two datapoints, if one or zero datapoint, this is a noop. + */ + void shrinkFlatAreas(const double); + + /** + Wrapper function for shrinkFlatAreas(const double) + providing a default epsilon parameter + */ + void shrinkFlatAreas() { + shrinkFlatAreas(1e-14); + }; + + + +private: + + // Data structure to store x- and f-values + std::map data; + + // Data structure to store x- and d-values + mutable std::map ddata; + + + // Storage containers for precomputed interpolation data + // std::vector dvalues; // derivatives in Hermite interpolation. + + // Flag to determine whether the boolean strictlyMonotone can be + // trusted. + mutable bool strictlyMonotoneCached; + mutable bool monotoneCached; /* only monotone, not stricly montone */ + + mutable bool strictlyMonotone; + mutable bool monotone; + + // if strictlyMonotone is true (and can be trusted), the two next are meaningful + mutable bool strictlyDecreasing; + mutable bool strictlyIncreasing; + mutable bool decreasing; + mutable bool increasing; + + + /* Hermite basis functions, t \in [0,1] , + notation from: + http://en.wikipedia.org/w/index.php?title=Cubic_Hermite_spline&oldid=84495502 + */ + + double H00(double t) const { + return 2*t*t*t - 3*t*t + 1; + } + double H10(double t) const { + return t*t*t - 2*t*t + t; + } + double H01(double t) const { + return -2*t*t*t + 3*t*t; + } + double H11(double t) const { + return t*t*t - t*t; + } + + + void computeInternalFunctionData() const ; + + /** + Computes initial derivative values using centered (second order) difference + for internal datapoints, and one-sided derivative for endpoints + + The internal datastructure map ddata is populated by this method. + */ + + void computeSimpleDerivatives() const ; + + + /** + Adjusts the derivative values (ddata) so that we can guarantee that + the resulting piecewise Hermite polymial is monotone. This is + done according to the algorithm of Fritsch and Carlsson 1980, + see Section 4, especially the two last lines. + */ + void adjustDerivativesForMonotoneness() const ; + + /** + Checks if the coefficient alpha and beta is in + the region that guarantees monotoneness of the + derivative values they represent + + See Fritsch and Carlson 1980, Lemma 2, + alternatively Step 5 in Wikipedia's article + on Monotone cubic interpolation. + */ + bool isMonotoneCoeff(double alpha, double beta) const { + if ((alpha*alpha + beta*beta) <= 9) { + return true; + } else { + return false; + } + } + +}; + + +#endif diff --git a/opm/core/utility/RootFinders.hpp b/opm/core/utility/RootFinders.hpp new file mode 100644 index 00000000..3011be18 --- /dev/null +++ b/opm/core/utility/RootFinders.hpp @@ -0,0 +1,178 @@ +//=========================================================================== +// +// File: RootFinders.hpp +// +// Created: Thu May 6 19:59:42 2010 +// +// Author(s): Atgeirr F Rasmussen +// Jostein R Natvig +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* + Copyright 2010 SINTEF ICT, Applied Mathematics. + Copyright 2010 Statoil ASA. + + This file is part of The Open Reservoir Simulator Project (OpenRS). + + OpenRS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenRS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenRS. If not, see . +*/ + +#ifndef OPENRS_ROOTFINDERS_HEADER +#define OPENRS_ROOTFINDERS_HEADER + + + +//#include +//#include +//#include +//#include +#include + +namespace Dune +{ + + inline double regulaFalsiStep(const double a, + const double b, + const double fa, + const double fb) + { + ASSERT(fa*fb < 0.0); + return (b*fa - a*fb)/(fa - fb); + } + + + /// Implements a modified regula falsi method as described in + /// "Improved algorithms of Illinois-type for the numerical + /// solution of nonlinear equations" + /// by J. A. Ford. + /// Current variant is the 'Pegasus' method. + template + inline double modifiedRegulaFalsi(const Functor& f, + const double a, + const double b, + const int max_iter, + const double tolerance, + int& iterations_used) + { + using namespace std; + const double macheps = numeric_limits::epsilon(); + const double eps = tolerance + macheps*max(max(fabs(a), fabs(b)), 1.0); + + double x0 = a; + double x1 = b; + double f0 = f(x0); + const double epsF = tolerance + macheps*max(fabs(f0), 1.0); + if (fabs(f0) < epsF) { + return x0; + } + double f1 = f(x1); + if (fabs(f1) < epsF) { + return x1; + } + if (f0*f1 > 0.0) { + THROW("Error in parameters, zero not bracketed: [a, b] = [" + << a << ", " << b << "] fa = " << f0 << " fb = " << f1); + } + iterations_used = 0; + // In every iteraton, x1 is the last point computed, + // and x0 is the last point computed that makes it a bracket. + while (fabs(x1 - x0) >= 0.95*eps) { + double xnew = regulaFalsiStep(x0, x1, f0, f1); + double fnew = f(xnew); +// cout << "xnew = " << xnew << " fnew = " << fnew << endl; + ++iterations_used; + if (iterations_used > max_iter) { + THROW("Maximum number of iterations exceeded.\n" + << "Current interval is [" << min(x0, x1) << ", " + << max(x0, x1) << "]"); + } + if (fabs(fnew) < epsF) { + return xnew; + } + // Now we must check which point we must replace. + if ((fnew > 0.0) == (f0 > 0.0)) { + // We must replace x0. + x0 = x1; + f0 = f1; + } else { + // We must replace x1, this is the case where + // the modification to regula falsi kicks in, + // by modifying f0. + // 1. The classic Illinois method +// const double gamma = 0.5; + // @afr: The next two methods do not work??!!? + // 2. The method called 'Method 3' in the paper. +// const double phi0 = f1/f0; +// const double phi1 = fnew/f1; +// const double gamma = 1.0 - phi1/(1.0 - phi0); + // 3. The method called 'Method 4' in the paper. +// const double phi0 = f1/f0; +// const double phi1 = fnew/f1; +// const double gamma = 1.0 - phi0 - phi1; +// cout << "phi0 = " << phi0 <<" phi1 = " << phi1 << +// " gamma = " << gamma << endl; + // 4. The 'Pegasus' method + const double gamma = f1/(f1 + fnew); + f0 *= gamma; + } + x1 = xnew; + f1 = fnew; + } + return 0.5*(x0 + x1); + } + + + template + inline void bracketZero(const Functor& f, + const double x0, + const double dx, + double& a, + double& b) + { + const int max_iters = 100; + double f0 = f(x0); + double cur_dx = dx; + int i = 0; + for (; i < max_iters; ++i) { + double x = x0 + cur_dx; + double f_new = f(x); + if (f0*f_new <= 0.0) { + break; + } + cur_dx = -2.0*cur_dx; + } + if (i == max_iters) { + THROW("Could not bracket zero in " << max_iters << "iterations."); + } + if (cur_dx < 0.0) { + a = x0 + cur_dx; + b = i < 2 ? x0 : x0 + 0.25*cur_dx; + } else { + a = i < 2 ? x0 : x0 + 0.25*cur_dx; + b = x0 + cur_dx; + } + } + + +} // namespace Dune + + + + +#endif // OPENRS_ROOTFINDERS_HEADER diff --git a/opm/core/utility/SparseTable.hpp b/opm/core/utility/SparseTable.hpp new file mode 100644 index 00000000..b7d2ec95 --- /dev/null +++ b/opm/core/utility/SparseTable.hpp @@ -0,0 +1,242 @@ +//=========================================================================== +// +// File: SparseTable.hpp +// +// Created: Fri Apr 24 09:50:27 2009 +// +// Author(s): Atgeirr F Rasmussen +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* + Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. + Copyright 2009, 2010 Statoil ASA. + + This file is part of The Open Reservoir Simulator Project (OpenRS). + + OpenRS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenRS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenRS. If not, see . +*/ + +#ifndef OPENRS_SPARSETABLE_HEADER +#define OPENRS_SPARSETABLE_HEADER + +#include +#include +#include +#include +#include "ErrorMacros.hpp" + +#include + +namespace Dune +{ + + /// A SparseTable stores a table with rows of varying size + /// as efficiently as possible. + /// It is supposed to behave similarly to a vector of vectors. + /// Its behaviour is similar to compressed row sparse matrices. + template + class SparseTable + { + public: + /// Default constructor. Yields an empty SparseTable. + SparseTable() + { + } + + /// A constructor taking all the data for the table and row sizes. + /// \param data_beg The start of the table data. + /// \param data_end One-beyond-end of the table data. + /// \param rowsize_beg The start of the row length data. + /// \param rowsize_end One beyond the end of the row length data. + template + SparseTable(DataIter data_beg, DataIter data_end, + IntegerIter rowsize_beg, IntegerIter rowsize_end) + : data_(data_beg, data_end) + { + setRowStartsFromSizes(rowsize_beg, rowsize_end); + } + + + /// Sets the table to contain the given data, organized into + /// rows as indicated by the given row sizes. + /// \param data_beg The start of the table data. + /// \param data_end One-beyond-end of the table data. + /// \param rowsize_beg The start of the row length data. + /// \param rowsize_end One beyond the end of the row length data. + template + void assign(DataIter data_beg, DataIter data_end, + IntegerIter rowsize_beg, IntegerIter rowsize_end) + { + data_.assign(data_beg, data_end); + setRowStartsFromSizes(rowsize_beg, rowsize_end); + } + + + /// Request storage for table of given size. + /// \param rowsize_beg Start of row size data. + /// \param rowsize_end One beyond end of row size data. + template + void allocate(IntegerIter rowsize_beg, IntegerIter rowsize_end) + { + typedef typename std::vector::size_type sz_t; + + sz_t ndata = std::accumulate(rowsize_beg, rowsize_end, sz_t(0)); + data_.resize(ndata); + setRowStartsFromSizes(rowsize_beg, rowsize_end); + } + + + /// Appends a row to the table. + template + void appendRow(DataIter row_beg, DataIter row_end) + { + data_.insert(data_.end(), row_beg, row_end); + if (row_start_.empty()) { + row_start_.reserve(2); + row_start_.push_back(0); + } + row_start_.push_back(data_.size()); + } + + /// True if the table contains no rows. + bool empty() const + { + return row_start_.empty(); + } + + /// Returns the number of rows in the table. + int size() const + { + return empty() ? 0 : row_start_.size() - 1; + } + + /// Allocate storage for table of expected size + void reserve(int exptd_nrows, int exptd_ndata) + { + row_start_.reserve(exptd_nrows + 1); + data_.reserve(exptd_ndata); + } + + /// Swap contents for other SparseTable + void swap(SparseTable& other) + { + row_start_.swap(other.row_start_); + data_.swap(other.data_); + } + + /// Returns the number of data elements. + int dataSize() const + { + return data_.size(); + } + + /// Returns the size of a table row. + int rowSize(int row) const + { + ASSERT(row >= 0 && row < size()); + return row_start_[row + 1] - row_start_[row]; + } + + /// Makes the table empty(). + void clear() + { + data_.clear(); + row_start_.clear(); + } + + /// Defining the row type, returned by operator[]. + typedef boost::iterator_range row_type; + typedef boost::iterator_range mutable_row_type; + + /// Returns a row of the table. + row_type operator[](int row) const + { + ASSERT(row >= 0 && row < size()); + const T* start_ptr = data_.empty() ? 0 : &data_[0]; + return row_type(start_ptr + row_start_[row], start_ptr + row_start_[row + 1]); + } + + /// Returns a mutable row of the table. + mutable_row_type operator[](int row) + { + ASSERT(row >= 0 && row < size()); + T* start_ptr = data_.empty() ? 0 : &data_[0]; + return mutable_row_type(start_ptr + row_start_[row], start_ptr + row_start_[row + 1]); + } + + /// Equality. + bool operator==(const SparseTable& other) const + { + return data_ == other.data_ && row_start_ == other.row_start_; + } + + template + void print(std::basic_ostream& os) const + { + os << "Number of rows: " << size() << '\n'; + + os << "Row starts = ["; + std::copy(row_start_.begin(), row_start_.end(), + std::ostream_iterator(os, " ")); + os << "\b]\n"; + + os << "Data values = ["; + std::copy(data_.begin(), data_.end(), + std::ostream_iterator(os, " ")); + os << "\b]\n"; + } + const T data(int i)const { + return data_[i]; + } + + private: + std::vector data_; + // Like in the compressed row sparse matrix format, + // row_start_.size() is equal to the number of rows + 1. + std::vector row_start_; + + template + void setRowStartsFromSizes(IntegerIter rowsize_beg, IntegerIter rowsize_end) + { + // Since we do not store the row sizes, but cumulative row sizes, + // we have to create the cumulative ones. + int num_rows = rowsize_end - rowsize_beg; + if (num_rows < 1) { + THROW("Must have at least one row. Got " << num_rows << " rows."); + } +#ifndef NDEBUG + if (*std::min_element(rowsize_beg, rowsize_end) < 0) { + THROW("All row sizes must be at least 0."); + } +#endif + row_start_.resize(num_rows + 1); + row_start_[0] = 0; + std::partial_sum(rowsize_beg, rowsize_end, row_start_.begin() + 1); + // Check that data_ and row_start_ match. + if (int(data_.size()) != row_start_.back()) { + THROW("End of row start indices different from data size."); + } + + } + }; + +} // namespace Dune + + +#endif // OPENRS_SPARSETABLE_HEADER diff --git a/opm/core/utility/SparseVector.hpp b/opm/core/utility/SparseVector.hpp new file mode 100644 index 00000000..bce407d6 --- /dev/null +++ b/opm/core/utility/SparseVector.hpp @@ -0,0 +1,195 @@ +//=========================================================================== +// +// File: SparseVector.hpp +// +// Created: Mon Jun 29 15:28:59 2009 +// +// Author(s): Atgeirr F Rasmussen +// Bård Skaflestad +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* + Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. + Copyright 2009, 2010 Statoil ASA. + + This file is part of The Open Reservoir Simulator Project (OpenRS). + + OpenRS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenRS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenRS. If not, see . +*/ + + +#ifndef OPENRS_SPARSEVECTOR_HEADER +#define OPENRS_SPARSEVECTOR_HEADER + +#include +#include +#include +#include +#include "ErrorMacros.hpp" + +namespace Dune +{ + + /// A SparseVector stores a vector with possibly many empty elements + /// as efficiently as possible. + /// It is supposed to behave similarly to a standard vector, but since + /// direct indexing is a O(log n) operation instead of O(1), we do not + /// supply it as operator[]. + template + class SparseVector + { + public: + /// Default constructor. Yields an empty SparseVector. + SparseVector() + : size_(0), default_elem_() + { + } + + /// Constructs a SparseVector with a given size, but no nonzero + /// elements. + explicit SparseVector(int size) + : size_(size), default_elem_() + { + } + + /// A constructor taking all the element data for the vector and their indices. + /// \param data_beg The start of the element data. + /// \param data_end One-beyond-end of the element data. + /// \param rowsize_beg The start of the index data. + /// \param rowsize_end One beyond the end of the index data. + template + SparseVector(int size, + DataIter data_beg, DataIter data_end, + IntegerIter index_beg, IntegerIter index_end) + : size_(size), data_(data_beg, data_end), indices_(index_beg, index_end), + default_elem_() + { +#ifndef NDEBUG + ASSERT(size >= 0); + ASSERT(indices_.size() == data_.size()); + int last_index = -1; + int num_ind = indices_.size(); + for (int i = 0; i < num_ind; ++i) { + int index = indices_[i]; + if (index <= last_index || index >= size) { + THROW("Error in SparseVector construction, index is nonincreasing or out of range."); + } + last_index = index; + } +#endif + } + + + /// Appends an element to the vector. Note that this function does not + /// increase the size() of the vector, it just adds another nonzero element. + /// Elements must be added in index order. + void addElement(const T& element, int index) + { + ASSERT(indices_.empty() || index > indices_.back()); + ASSERT(index < size_); + data_.push_back(element); + indices_.push_back(index); + } + + /// \return true if the vector has size 0. + bool empty() const + { + return size_ == 0; + } + + /// Returns the size of the vector. + /// Recall that most or all of the vector may be default/zero. + int size() const + { + return size_; + } + + /// Returns the number of nonzero data elements. + int nonzeroSize() const + { + return data_.size(); + } + + /// Makes the vector empty(). + void clear() + { + data_.clear(); + indices_.clear(); + size_ = 0; + } + + /// Equality. + bool operator==(const SparseVector& other) const + { + return size_ == other.size_ && data_ == other.data_ && indices_ == other.indices_; + } + + /// O(log n) element access. + /// \param index the proper vector index + /// \return the element with the given index, or the default element if no element in + /// the vector has the given index. + const T& element(int index) const + { + ASSERT(index >= 0); + ASSERT(index < size_); + std::vector::const_iterator lb = std::lower_bound(indices_.begin(), indices_.end(), index); + if (lb != indices_.end() && *lb == index) { + return data_[lb - indices_.begin()]; + } else { + return default_elem_; + } + } + + /// O(1) element access. + /// \param nzindex an index counting only nonzero elements. + /// \return the nzindex'th nonzero element. + const T& nonzeroElement(int nzindex) const + { + ASSERT(nzindex >= 0); + ASSERT(nzindex < nonzeroSize()); + return data_[nzindex]; + } + + /// O(1) index access. + /// \param nzindex an index counting only nonzero elements. + /// \return the index of the nzindex'th nonzero element. + int nonzeroIndex(int nzindex) const + { + ASSERT(nzindex >= 0); + ASSERT(nzindex < nonzeroSize()); + return indices_[nzindex]; + } + + private: + // The vectors data_ and indices_ are always the same size. + // The indices are supposed to be stored in increasing order, + // to be unique, and to be in [0, size_ - 1]. + // default_elem_ is returned when a default element is requested. + int size_; + std::vector data_; + std::vector indices_; + T default_elem_; + }; + +} // namespace Dune + + + + +#endif // OPENRS_SPARSEVECTOR_HEADER diff --git a/opm/core/utility/StopWatch.cpp b/opm/core/utility/StopWatch.cpp new file mode 100644 index 00000000..1d5a2bbd --- /dev/null +++ b/opm/core/utility/StopWatch.cpp @@ -0,0 +1,106 @@ +//=========================================================================== +// +// File: StopWatch.cpp +// +// Created: Thu Jul 2 23:04:51 2009 +// +// Author(s): Atgeirr F Rasmussen +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* + Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. + Copyright 2009, 2010 Statoil ASA. + + This file is part of The Open Reservoir Simulator Project (OpenRS). + + OpenRS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenRS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenRS. If not, see . +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include "StopWatch.hpp" +#include "ErrorMacros.hpp" + +namespace Dune +{ + + namespace time + { + + StopWatch::StopWatch() + : state_(UnStarted) + { + } + + + void StopWatch::start() + { + start_time_ = boost::posix_time::microsec_clock::local_time(); + last_time_ = start_time_; + state_ = Running; + } + + void StopWatch::stop() + { + if (state_ != Running) { + THROW("Called stop() on a StopWatch that was not running."); + } + stop_time_ = boost::posix_time::microsec_clock::local_time(); + state_ = Stopped; + } + + double StopWatch::secsSinceLast() + { + boost::posix_time::ptime run_time; + if (state_ == Running) { + run_time = boost::posix_time::microsec_clock::local_time(); + } else if (state_ == Stopped) { + run_time = stop_time_; + } else { + ASSERT(state_ == UnStarted); + THROW("Called secsSinceLast() on a StopWatch that had not been started."); + } + boost::posix_time::time_duration dur = run_time - last_time_; + last_time_ = run_time; + return double(dur.total_microseconds())/1000000.0; + } + + double StopWatch::secsSinceStart() + { + boost::posix_time::ptime run_time; + if (state_ == Running) { + run_time = boost::posix_time::microsec_clock::local_time(); + } else if (state_ == Stopped) { + run_time = stop_time_; + } else { + ASSERT(state_ == UnStarted); + THROW("Called secsSinceStart() on a StopWatch that had not been started."); + } + boost::posix_time::time_duration dur = run_time - start_time_; + return double(dur.total_microseconds())/1000000.0; + } + + } // namespace time + +} // namespace Dune + + + diff --git a/opm/core/utility/StopWatch.hpp b/opm/core/utility/StopWatch.hpp new file mode 100644 index 00000000..50106413 --- /dev/null +++ b/opm/core/utility/StopWatch.hpp @@ -0,0 +1,81 @@ +//=========================================================================== +// +// File: StopWatch.hpp +// +// Created: Thu Jul 2 23:04:17 2009 +// +// Author(s): Atgeirr F Rasmussen +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* + Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. + Copyright 2009, 2010 Statoil ASA. + + This file is part of The Open Reservoir Simulator Project (OpenRS). + + OpenRS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenRS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenRS. If not, see . +*/ + +#ifndef OPENRS_STOPWATCH_HEADER +#define OPENRS_STOPWATCH_HEADER + +#include + +namespace Dune +{ + + namespace time + { + + class StopWatch + { + public: + /// Default constructor. Before the StopWatch is start()-ed, + /// it is an error to call anything other than start(). + StopWatch(); + + /// Starts the StopWatch. It is always legal to call + /// start(), even if not stop()-ped. + void start(); + /// Stops the StopWatch. The watch no longer runs, until + /// restarted by a call to start(). + void stop(); + + /// \ret the number of running seconds that have passed + /// since last call to start(), secsSinceLast() or + /// secsSinceStart() + double secsSinceLast(); + /// \ret the number of running seconds that have passed + /// since last call to start(). + double secsSinceStart(); + + private: + enum StopWatchState { UnStarted, Running, Stopped }; + + StopWatchState state_; + boost::posix_time::ptime start_time_; + boost::posix_time::ptime last_time_; + boost::posix_time::ptime stop_time_; + }; + + } // namespace time + +} // namespace Dune + +#endif // OPENRS_STOPWATCH_HEADER diff --git a/opm/core/utility/Units.hpp b/opm/core/utility/Units.hpp new file mode 100644 index 00000000..34e46d8c --- /dev/null +++ b/opm/core/utility/Units.hpp @@ -0,0 +1,182 @@ +//=========================================================================== +// +// File: Units.hpp +// +// Created: Thu Jul 2 09:19:08 2009 +// +// Author(s): Halvor M Nilsen +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* + Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. + Copyright 2009, 2010 Statoil ASA. + + This file is part of The Open Reservoir Simulator Project (OpenRS). + + OpenRS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenRS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenRS. If not, see . +*/ + +#ifndef OPENRS_UNITS_HEADER +#define OPENRS_UNITS_HEADER + +namespace Dune +{ + namespace prefix + { + const double micro = 1.0e-6; + const double milli = 1.0e-3; + const double centi = 1.0e-2; + const double deci = 1.0e-1; + const double kilo = 1.0e3; + const double mega = 1.0e6; + const double giga = 1.0e9; + } // namespace prefix + + namespace unit + { + // Common powers + inline double square(double v) { return v * v; } + inline double cubic (double v) { return v * v * v; } + + // -------------------------------------------------------------- + // Basic (fundamental) units and conversions + // -------------------------------------------------------------- + + // Length: + const double meter = 1; + const double inch = 2.54 * prefix::centi*meter; + const double feet = 12 * inch; + + // Time: + const double second = 1; + const double minute = 60 * second; + const double hour = 60 * minute; + const double day = 24 * hour; + const double year = 365 * day; + + // Volume + const double stb = 0.158987294928 * cubic(meter); + + + // Mass: + const double kilogram = 1; + + // http://en.wikipedia.org/wiki/Pound_(mass)#Avoirdupois_pound + const double pound = 0.45359237 * kilogram; + + // -------------------------------------------------------------- + // Standardised constants + // -------------------------------------------------------------- + + const double gravity = 9.80665 * meter/square(second); + + // -------------------------------------------------------------- + // Derived units and conversions + // -------------------------------------------------------------- + + // Force: + const double Newton = kilogram*meter / square(second); // == 1 + const double lbf = pound * gravity; // Pound-force + + // Pressure: + const double Pascal = Newton / square(meter); // == 1 + const double barsa = 100000 * Pascal; + const double atm = 101325 * Pascal; + const double psia = lbf / square(inch); + + // Viscosity: + const double Pas = Pascal * second; // == 1 + const double Poise = prefix::deci*Pas; + + // Permeability: + // + // A porous medium with a permeability of 1 darcy permits a + // flow (flux) of 1 cm³/s of a fluid with viscosity 1 cP (1 + // mPa·s) under a pressure gradient of 1 atm/cm acting across + // an area of 1 cm². + // + namespace perm_details { + const double p_grad = atm / (prefix::centi*meter); + const double area = square(prefix::centi*meter); + const double flux = cubic (prefix::centi*meter) / second; + const double velocity = flux / area; + const double visc = prefix::centi*Poise; + const double darcy = (velocity * visc) / p_grad; + // == 1e-7 [m^2] / 101325 + // == 9.869232667160130e-13 [m^2] + } + const double darcy = perm_details::darcy; + + // Unit conversion support. + // + // Note: Under the penalty of treason will you be + // + // using namespace Dune::unit::convert; + // + // I mean it! + // + namespace convert { + // Convert from external units of measurements to equivalent + // internal units of measurements. Note: The internal units + // of measurements are *ALWAYS*, and exclusively, SI. + // + // Example: Convert a double kx, containing a permeability + // value in units of milli-darcy (mD) to the equivalent + // value in SI units (m^2). + // + // using namespace Dune::unit; + // using namespace Dune::prefix; + // convert::from(kx, milli*darcy); + // + inline double from(const double q, const double unit) + { + return q * unit; + } + + // Convert from internal units of measurements to equivalent + // external units of measurements. Note: The internal units + // of measurements are *ALWAYS*, and exclusively, SI. + // + // Example: Convert a std::vector p, containing + // pressure values in the SI unit Pascal (i.e., unit::Pascal) + // to the equivalent values in Psi (unit::psia). + // + // using namespace Dune::unit; + // std::transform(p.begin(), p.end(), p.begin(), + // boost::bind(convert::to, _1, psia)); + // + inline double to(const double q, const double unit) + { + return q / unit; + } + } // namespace convert + } // namespace unit + + namespace units { + // const double MILLIDARCY = 1.0;//9.86923e-16; + // const double VISCOSITY_UNIT = 1.0;//1e-3; + // const double DAYS2SECONDS = 1.0;//86400; + const double MILLIDARCY = 9.86923e-16; + const double VISCOSITY_UNIT = 1e-3; + const double DAYS2SECONDS = 86400; + const double FEET = 0.30479999798832; + const double WELL_INDEX_UNIT = VISCOSITY_UNIT/(DAYS2SECONDS*1e5); + } // namespace units +} // namespace Dune +#endif // OPENRS_UNITS_HEADER diff --git a/opm/core/utility/linInt.hpp b/opm/core/utility/linInt.hpp new file mode 100644 index 00000000..164395c1 --- /dev/null +++ b/opm/core/utility/linInt.hpp @@ -0,0 +1,117 @@ +//=========================================================================== +// +// File: linInt.hpp +// +// Created: Tue Feb 16 14:44:10 2010 +// +// Author(s): Bjørn Spjelkavik +// Atgeirr F Rasmussen +// Bård Skaflestad +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* + Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. + Copyright 2009, 2010 Statoil ASA. + + This file is part of The Open Reservoir Simulator Project (OpenRS). + + OpenRS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenRS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenRS. If not, see . +*/ + +#ifndef OPENRS_LININT_HEADER +#define OPENRS_LININT_HEADER + +#include +#include + +namespace Dune +{ + + inline int tableIndex(const std::vector& table, double x) + { + // Returns an index in an ordered table such that x is between + // table[j] and table[j+1]. If x is out of range, first or last + // interval is returned; Binary search. + int n = table.size() - 1; + if (n < 2) { + return 0; + } + int jl = 0; + int ju = n; + bool ascend = (table[n] > table[0]); + while (ju - jl > 1) { + int jm = (ju + jl)/2; // Compute a midpoint + if ( (x >= table[jm]) == ascend) { + jl = jm; // Replace lower limit + } else { + ju = jm; // Replace upper limit + } + } + return jl; + } + + inline double linearInterpolation(const std::vector& xv, + const std::vector& yv, double x) + { + // Returns end point if x is outside xv + std::vector::const_iterator lb = lower_bound(xv.begin(), xv.end(), x); + int ix2 = lb - xv.begin(); + if (ix2 == 0) { + return yv[0]; + } else if (ix2 == int(xv.size())) { + return yv[ix2-1]; + } + int ix1 = ix2 - 1; + return (yv[ix2] - yv[ix1])/(xv[ix2] - xv[ix1])*(x - xv[ix1]) + yv[ix1]; + } + + inline double linearInterpolDerivative(const std::vector& xv, + const std::vector& yv, double x) + { + int ix1 = tableIndex(xv, x); + int ix2 = ix1 + 1; + return (yv[ix2] - yv[ix1])/(xv[ix2] - xv[ix1]); + } + + inline double linearInterpolationExtrap(const std::vector& xv, + const std::vector& yv, double x) + { + // Extrapolates if x is outside xv + int ix1 = tableIndex(xv, x); + int ix2 = ix1 + 1; + return (yv[ix2] - yv[ix1])/(xv[ix2] - xv[ix1])*(x - xv[ix1]) + yv[ix1]; + } + + inline double linearInterpolationExtrap(const std::vector& xv, + const std::vector& yv, + double x, int& ix1) + { + // Extrapolates if x is outside xv + ix1 = tableIndex(xv, x); + int ix2 = ix1 + 1; + return (yv[ix2] - yv[ix1])/(xv[ix2] - xv[ix1])*(x - xv[ix1]) + yv[ix1]; + } + +} // namespace Dune + + + + + +#endif // OPENRS_LININT_HEADER diff --git a/opm/core/utility/parameters/Makefile.am b/opm/core/utility/parameters/Makefile.am new file mode 100644 index 00000000..7eb2ffc2 --- /dev/null +++ b/opm/core/utility/parameters/Makefile.am @@ -0,0 +1,21 @@ +# $Date$ +# $Revision$ + +SUBDIRS = tinyxml . test + +paramdir = $(includedir)/dune/common/param + +param_HEADERS = Parameter.hpp ParameterGroup.hpp ParameterGroup_impl.hpp \ + ParameterMapItem.hpp ParameterRequirement.hpp \ + ParameterStrings.hpp ParameterTools.hpp ParameterXML.hpp + +noinst_LTLIBRARIES = libparam.la + +libparam_la_SOURCES = Parameter.cpp ParameterGroup.cpp ParameterTools.cpp \ + ParameterXML.cpp + +libparam_la_CPPFLAGS = $(ALL_PKG_CPPFLAGS) $(BOOST_CPPFLAGS) +libparam_la_LDFLAGS = $(ALL_PKG_LDFLAGS) $(BOOST_LDFLAGS) +libparam_la_LIBADD = tinyxml/libtinyxml.la $(ALL_PKG_LIBS) $(BOOST_FILESYSTEM_LIB) $(BOOST_SYSTEM_LIB) + +include $(top_srcdir)/am/global-rules diff --git a/opm/core/utility/parameters/Parameter.cpp b/opm/core/utility/parameters/Parameter.cpp new file mode 100644 index 00000000..dfb27b9d --- /dev/null +++ b/opm/core/utility/parameters/Parameter.cpp @@ -0,0 +1,74 @@ +//=========================================================================== +// +// File: Parameter.cpp +// +// Created: Tue Jun 2 19:18:25 2009 +// +// Author(s): Bård Skaflestad +// Atgeirr F Rasmussen +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* +Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. +Copyright 2009, 2010 Statoil ASA. + +This file is part of The Open Reservoir Simulator Project (OpenRS). + +OpenRS is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenRS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OpenRS. If not, see . +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include + +namespace Dune { + namespace parameter { + std::string + correct_parameter_tag(const ParameterMapItem& item) + { + std::string tag = item.getTag(); + if (tag != ID_xmltag__param) { + std::string error = "The XML tag was '" + + tag + "' but should be '" + + ID_xmltag__param + "'.\n"; + return error; + } else { + return ""; + } + } + + std::string + correct_type(const Parameter& parameter, + const std::string& param_type) + { + std::string type = parameter.getType(); + if ( (type != param_type) && + (type != ID_param_type__cmdline) ) { + std::string error = "The data was of type '" + type + + "' but should be of type '" + + param_type + "'.\n"; + return error; + } else { + return ""; + } + } + } // namespace parameter +} // namespace Dune diff --git a/opm/core/utility/parameters/Parameter.hpp b/opm/core/utility/parameters/Parameter.hpp new file mode 100644 index 00000000..078e7416 --- /dev/null +++ b/opm/core/utility/parameters/Parameter.hpp @@ -0,0 +1,214 @@ +//=========================================================================== +// +// File: Parameter.hpp +// +// Created: Tue Jun 2 16:00:21 2009 +// +// Author(s): Bård Skaflestad +// Atgeirr F Rasmussen +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* +Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. +Copyright 2009, 2010 Statoil ASA. + +This file is part of The Open Reservoir Simulator Project (OpenRS). + +OpenRS is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenRS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OpenRS. If not, see . +*/ + +#ifndef OPENRS_PARAMETER_HEADER +#define OPENRS_PARAMETER_HEADER + +#include +#include + +#include +#include + +namespace Dune { + /// See ParameterGroup.hpp for how to use the parameter system + namespace parameter { + /// @brief + /// @todo Doc me! + class Parameter : public ParameterMapItem { + public: + /// @brief + /// @todo Doc me! + virtual ~Parameter() {} + /// @brief + /// @todo Doc me! + /// @return + virtual std::string getTag() const {return ID_xmltag__param;} + /// @brief + /// @todo Doc me! + /// @param + Parameter(const std::string& value, const std::string& type) + : value_(value), type_(type) {} + /// @brief + /// @todo Doc me! + /// @return + std::string getValue() const {return value_;} + /// @brief + /// @todo Doc me! + /// @return + std::string getType() const {return type_;} + private: + std::string value_; + std::string type_; + }; + + /// @brief + /// @todo Doc me! + /// @param + /// @return + std::string correct_parameter_tag(const ParameterMapItem& item); + std::string correct_type(const Parameter& parameter, + const std::string& type); + + /// @brief + /// @todo Doc me! + /// @tparam + /// @param + /// @return + template<> + struct ParameterMapItemTrait { + static int convert(const ParameterMapItem& item, + std::string& conversion_error) + { + conversion_error = correct_parameter_tag(item); + if (conversion_error != "") { + return 0; + } + const Parameter& parameter = dynamic_cast(item); + conversion_error = correct_type(parameter, ID_param_type__int); + if (conversion_error != "") { + return 0; + } + std::stringstream stream; + stream << parameter.getValue(); + int value; + stream >> value; + if (stream.fail()) { + conversion_error = "Conversion to '" + + ID_param_type__int + + "' failed. Data was '" + + parameter.getValue() + "'.\n"; + return 0; + } + return value; + } + static std::string type() {return ID_param_type__int;} + }; + + /// @brief + /// @todo Doc me! + /// @tparam + /// @param + /// @return + template<> + struct ParameterMapItemTrait { + static double convert(const ParameterMapItem& item, + std::string& conversion_error) + { + conversion_error = correct_parameter_tag(item); + if (conversion_error != "") { + return 0.0; + } + const Parameter& parameter = dynamic_cast(item); + conversion_error = correct_type(parameter, ID_param_type__float); + if (conversion_error != "") { + return 0.0; + } + std::stringstream stream; + stream << parameter.getValue(); + double value; + stream >> value; + if (stream.fail()) { + conversion_error = "Conversion to '" + + ID_param_type__float + + "' failed. Data was '" + + parameter.getValue() + "'.\n"; + return 0.0; + } + return value; + } + static std::string type() {return ID_param_type__float;} + }; + + /// @brief + /// @todo Doc me! + /// @tparam + /// @param + /// @return + template<> + struct ParameterMapItemTrait { + static bool convert(const ParameterMapItem& item, + std::string& conversion_error) + { + conversion_error = correct_parameter_tag(item); + if (conversion_error != "") { + return false; + } + const Parameter& parameter = dynamic_cast(item); + conversion_error = correct_type(parameter, ID_param_type__bool); + if (conversion_error != "") { + return false; + } + if (parameter.getValue() == ID_true) { + return true; + } else if (parameter.getValue() == ID_false) { + return false; + } else { + conversion_error = "Conversion failed. Data was '" + + parameter.getValue() + + "', but should be one of '" + + ID_true + "' or '" + ID_false + "'.\n"; + return false; + } + } + static std::string type() {return ID_param_type__bool;} + }; + + /// @brief + /// @todo Doc me! + /// @tparam + /// @param + /// @return + template<> + struct ParameterMapItemTrait { + static std::string convert(const ParameterMapItem& item, + std::string& conversion_error) + { + conversion_error = correct_parameter_tag(item); + if (conversion_error != "") { + return ""; + } + const Parameter& parameter = dynamic_cast(item); + conversion_error = correct_type(parameter, ID_param_type__string); + if (conversion_error != "") { + return ""; + } + return parameter.getValue(); + } + static std::string type() {return ID_param_type__string;} + }; + } // namespace parameter +} // namespace Dune +#endif // OPENRS_PARAMETER_HPP diff --git a/opm/core/utility/parameters/ParameterGroup.cpp b/opm/core/utility/parameters/ParameterGroup.cpp new file mode 100644 index 00000000..bc6ca9f4 --- /dev/null +++ b/opm/core/utility/parameters/ParameterGroup.cpp @@ -0,0 +1,329 @@ +//=========================================================================== +// +// File: ParameterGroup.cpp +// +// Created: Tue Jun 2 19:13:17 2009 +// +// Author(s): Bård Skaflestad +// Atgeirr F Rasmussen +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* +Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. +Copyright 2009, 2010 Statoil ASA. + +This file is part of The Open Reservoir Simulator Project (OpenRS). + +OpenRS is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenRS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OpenRS. If not, see . +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace Dune { + namespace parameter { + + ParameterGroup::ParameterGroup() + : path_(ID_path_root), parent_(0), output_is_enabled_(true) + { + } + + ParameterGroup::~ParameterGroup() { + if (output_is_enabled_) { + //TermColors::Normal(); + } + } + + std::string ParameterGroup::getTag() const { + return ID_xmltag__param_grp; + } + + + bool ParameterGroup::has(const std::string& name) const + { + std::pair name_path = split(name); + map_type::const_iterator it = map_.find(name_path.first); + if (it == map_.end()) { + return false; + } else if (name_path.second == "") { + return true; + } else { + ParameterGroup& pg = dynamic_cast(*(*it).second); + return pg.has(name_path.second); + } + } + + std::string ParameterGroup::path() const { + return path_; + } + + ParameterGroup::ParameterGroup(const std::string& path, + const ParameterGroup* parent) + : path_(path), parent_(parent), output_is_enabled_(true) + { + } + + ParameterGroup + ParameterGroup::getGroup(const std::string& name) const + { + return get(name); + } + + void ParameterGroup::readXML(const std::string& xml_filename) + { + fill_xml(*this, xml_filename); + } + + namespace { + inline std::istream& + samcode_readline(std::istream& is, std::string& parameter) { + return std::getline(is, parameter); + } + } // anonymous namespace + + void ParameterGroup::readParam(const std::string& param_filename) { + std::ifstream is(param_filename.c_str()); + if (!is) { + std::cerr << "Error: Failed to open parameter file '" << param_filename << "'.\n"; + throw std::exception(); + } + std::string parameter; + int lineno = 0; + while (samcode_readline(is, parameter)) { + ++lineno; + int fpos = parameter.find(ID_delimiter_assignment); + if (fpos == int(std::string::npos)) { + std::cerr << "WARNING: No '" << ID_delimiter_assignment << "' found on line " << lineno << ".\n"; + } + int pos = fpos + ID_delimiter_assignment.size(); + int spos = parameter.find(ID_delimiter_assignment, pos); + if (spos == int(std::string::npos)) { + std::string name = parameter.substr(0, fpos); + std::string value = parameter.substr(pos, spos); + this->insertParameter(name, value); + } else { + std::cerr << "WARNING: To many '" << ID_delimiter_assignment << "' found on line " << lineno << ".\n"; + } + } + #ifdef MATLAB_MEX_FILE + fclose(is); + #endif + } + + void ParameterGroup::writeParam(const std::string& param_filename) const { + std::ofstream os(param_filename.c_str()); + if (!os) { + std::cerr << "Error: Failed to open parameter file '" + << param_filename << "'.\n"; + throw std::exception(); + } + this->writeParamToStream(os); + } + + void ParameterGroup::writeParamToStream(std::ostream& os) const + { + if (map_.empty()) { + os << this->path() << "/" << "dummy" + << ID_delimiter_assignment << "0\n"; + } + for (map_type::const_iterator it = map_.begin(); it != map_.end(); ++it) { + if ( (*it).second->getTag() == ID_xmltag__param_grp ) { + ParameterGroup& pg = dynamic_cast(*(*it).second); + pg.writeParamToStream(os); + } else if ( (*it).second->getTag() == ID_xmltag__param) { + os << this->path() << "/" << (*it).first << ID_delimiter_assignment + << dynamic_cast(*(*it).second).getValue() << "\n"; + } else { + os << this->path() << "/" << (*it).first << ID_delimiter_assignment + << "<" << (*it).second->getTag() << ">" << "\n"; + } + } + } + + void ParameterGroup::insert(const std::string& name, + const std::tr1::shared_ptr& data) + { + std::pair name_path = split(name); + map_type::const_iterator it = map_.find(name_path.first); + assert(name_path.second == ""); + if (it == map_.end()) { + map_[name] = data; + } else { + if ( (map_[name]->getTag() == data->getTag()) && + (data->getTag() == ID_xmltag__param_grp) ) { + ParameterGroup& alpha = dynamic_cast(*(*it).second); + ParameterGroup& beta = dynamic_cast(*data); + for (map_type::const_iterator + item = beta.map_.begin(); item != beta.map_.end(); ++item) { + alpha.insert((*item).first, (*item).second); + } + } else { + std::cout << "WARNING : The '" + << map_[name]->getTag() + << "' element '" + << name + << "' already exist in group '" + << this->path() + << "'. The element will be replaced by a '" + << data->getTag() + << "' element.\n"; + map_[name] = data; + } + } + } + + void ParameterGroup::insertParameter(const std::string& name, + const std::string& value) + { + std::pair name_path = split(name); + while (name_path.first == "") { + name_path = split(name_path.second); + } + map_type::const_iterator it = map_.find(name_path.first); + if (it == map_.end()) { + if (name_path.second == "") { + std::tr1::shared_ptr data(new Parameter(value, ID_param_type__cmdline)); + map_[name_path.first] = data; + } else { + std::tr1::shared_ptr data(new ParameterGroup(this->path() + ID_delimiter_path + name_path.first, + this)); + ParameterGroup& child = dynamic_cast(*data); + child.insertParameter(name_path.second, value); + map_[name_path.first] = data; + } + } else if (name_path.second == "") { + if ((*it).second->getTag() != ID_xmltag__param) { + std::cout << "WARNING : The " + << map_[name_path.first]->getTag() + << " element '" + << name + << "' already exist in group '" + << this->path() + << "'. It will be replaced by a " + << ID_xmltag__param + << " element.\n"; + } + std::tr1::shared_ptr data(new Parameter(value, ID_param_type__cmdline)); + map_[name_path.first] = data; + } else { + ParameterGroup& pg = dynamic_cast(*(*it).second); + pg.insertParameter(name_path.second, value); + } + } + +#if 0 + void ParameterGroup::display() const { + std::cout << "Begin: " << this->path() << "\n"; + for (map_type::const_iterator it = map_.begin(); it != map_.end(); ++it) { + if ( (*it).second->getTag() == ID_xmltag__param_grp ) { + ParameterGroup& pg = dynamic_cast(*(*it).second); + pg.display(); + } else if ( (*it).second->getTag() == ID_xmltag__param) { + std::cout << (*it).first + << " (" + << (*it).second->getTag() + << ") has value " + << dynamic_cast(*(*it).second).getValue() + << "\n"; + } else { + std::cout << (*it).first + << " (" + << (*it).second->getTag() + << ")\n"; + } + } + std::cout << "End: " << this->path() << "\n"; + } +#endif + + bool ParameterGroup::anyUnused() const + { + if (!this->used()) { + return true; + } + for (map_type::const_iterator it = map_.begin(); it != map_.end(); ++it) { + if (it->second->getTag() == ID_xmltag__param_grp) { + ParameterGroup& pg = dynamic_cast(*(it->second)); + if (pg.anyUnused()) { + return true; + } + } else if (it->second->getTag() == ID_xmltag__param) { + if (!it->second->used()) { + return true; + } + } + } + return false; + } + + void ParameterGroup::displayUsage(bool used_params) const + { + if (this->used() == used_params) { + std::cout << this->path() << '\n'; + } + for (map_type::const_iterator it = map_.begin(); it != map_.end(); ++it) { + if (it->second->getTag() == ID_xmltag__param_grp) { + ParameterGroup& pg = dynamic_cast(*(it->second)); + pg.displayUsage(used_params); + } else if (it->second->getTag() == ID_xmltag__param) { + if (it->second->used() == used_params) { + std::cout << path() << '/' << it->first << '\n'; + } + } + } + std::cout << std::flush; + } + + void ParameterGroup::disableOutput() { + this->recursiveSetIsOutputEnabled(false); + } + + void ParameterGroup::enableOutput() { + this->recursiveSetIsOutputEnabled(true); + } + + bool ParameterGroup::isOutputEnabled() const { + return output_is_enabled_; + } + + void ParameterGroup::recursiveSetIsOutputEnabled(bool output_is_enabled) + { + output_is_enabled_ = output_is_enabled; + for (map_type::const_iterator it = map_.begin(); it != map_.end(); ++it) { + if (it->second->getTag() == ID_xmltag__param_grp) { + ParameterGroup& pg = dynamic_cast(*(it->second)); + pg.recursiveSetIsOutputEnabled(output_is_enabled); + } + } + } + + } // namespace parameter +} // namespace Dune diff --git a/opm/core/utility/parameters/ParameterGroup.hpp b/opm/core/utility/parameters/ParameterGroup.hpp new file mode 100644 index 00000000..ecfe0684 --- /dev/null +++ b/opm/core/utility/parameters/ParameterGroup.hpp @@ -0,0 +1,292 @@ +//=========================================================================== +// +// File: ParameterGroup.hpp +// +// Created: Tue Jun 2 19:11:11 2009 +// +// Author(s): Bård Skaflestad +// Atgeirr F Rasmussen +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* +Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. +Copyright 2009, 2010 Statoil ASA. + +This file is part of The Open Reservoir Simulator Project (OpenRS). + +OpenRS is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenRS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OpenRS. If not, see . +*/ + +#ifndef OPENRS_PARAMETERGROUP_HEADER +#define OPENRS_PARAMETERGROUP_HEADER + +#include +#include +#include +#include +#include + +#include +#include + +namespace Dune { + namespace parameter { + /// ParameterGroup is a class that is used to provide run-time paramters. + /// The standard use of the class is to call create it with the + /// (int argc, char** argv) constructor (where the arguments are those + /// given by main). This parses the command line, where each token + /// either + /// A) specifies a parameter (by a "param=value" token). + /// B) specifies a xml file to be read (by a "filename.xml" token). + /// C) specifies a param file to be read (by a "filename.param" token). + /// After the tokens are parsem they are stored in a tree structure + /// in the ParameterGroup object; it is worth mentioning that parameters + /// are inherited in this tree structure. Thus, if ``grid\_prefix'' is + /// given a value in the root node, this value will be visible in all + /// parts of the tree (unless the parameter is overwritten in a subtree). + /// Applications using this ParameterGroup objects will usually write out + /// a message for each node in the three that is used by the application; + /// this is one way to determine valid parameters. + /// + /// Parameters specified as "param=value" on the command line + /// + /// To specify a parameter on the command line, you must know where in the tree the + /// parameter resides. The syntax for specifying parameters on the command line given + /// an application called ``simulator'' is + /// simulator param1=value grp/param2=value + // for parameter ``param1'' lying at the root and ``param2'' in the group + /// ``grp''. If the same parameters are specified multiple times on the command + /// line, only the last will be used. Thus an application named ``simulator'' run with + /// the following command + /// simulator param=value1 param=value2 + /// will use value2 as the value of ``param''. + /// + /// XML parameters + /// + /// In the introduction to this section it was mentioned that the parameters for + /// the simulator are organized in a tree structure. This is mirrored in the XML + /// file by the use of groups; a group is a collection of parameters and subgroups + /// enclosed by a \fixed{}-\fixed{} pair. The only + /// attribute of relevance in a \fixed{ParameterGroup} tag is that of \fixed{name}, + /// which is used to navigate in the tree. + /// The actual parameters are specified by the \fixed{} tag. Each + /// parameter has three attributes: \fixed{name}, \fixed{type}, and \fixed{value}. + /// Both name and value should be evident. Type is one of \fixed{bool} (for things + /// that are either true or false), \fixed{int} (for integers), \fixed{double} (for + /// floating point numbers with double precision), \fixed{string} (for text + /// strings), or \fixed{file} (for files and directories relative to the location + /// of the XML file). + /// + /// param files + /// + /// A param file consists of multiple lienes, where each line consists of "param=value". + /// This syntax is identical to the one for paramters specified on the command line. + /// + /// + /// If one combines both XML files and parameters, one should note that that if a parameter + /// is specified on the command line is also found in a XML file, the parameter + /// specified on the command line is the one used. Thus, if ``parameters.xml'' + /// specifies that ``stepsize'' is 2.71828 while, the command used to run the + /// application ``simulator'' is + /// simulator stepsize=3.14159 parameters.xml + /// the simulator will run with ``stepsize'' equal to 3.14159. + /// + class ParameterGroup : public ParameterMapItem { + public: + struct NotFoundException : public std::exception {}; + struct WrongTypeException : public std::exception {}; + struct ConversionFailedException : public std::exception {}; + + template + struct RequirementFailedException : public std::exception {}; + + ParameterGroup(); + ParameterGroup(const std::string& path, const ParameterGroup* parent); + + // From ParameterMapItem + virtual ~ParameterGroup(); + virtual std::string getTag() const; + + /// \brief A constructor typically used to initialize a + /// ParameterGroup from command-line arguments. + /// + /// It is required that argv[0] is the program name, while if + /// 0 < i < argc, then argv[i] is either + /// the name of an xml file or parametername=value. + /// + /// \param argc is the number of command-line arguments, + /// including the name of the executable. + /// \param argv is an array of char*, each of which is a + /// command line argument. + template + ParameterGroup(int argc, StringArray argv); + + /// \brief This method checks if there is something with name + /// \p name in the parameter gropup. + /// + /// \param name is the name of the parameter. + /// \return true if \p name is the name of something in the parameter + /// group, false otherwise. + bool has(const std::string& name) const; + + /// \brief This method is used to read a parameter from the + /// parameter group. + /// + /// NOTE: If the reading of the parameter fails, then this method + /// throws an appropriate exception. + /// + /// \param name is the name of the parameter in question. + /// \return The value associated with then name in this parameter + /// group. + template + T get(const std::string& name) const; + + template + T get(const std::string& name, const Requirement&) const; + + /// \brief This method is used to read a parameter from the + /// parameter group. + /// + /// NOTE: If the reading of the parameter fails, then either + /// a) this method returns \p default_value if there + /// was no parameter with name \p name in + /// the parameter group + /// or + /// b) this method throws an appropriate exception. + /// + /// \param name is the name of the parameter in question. + /// \param default_value the defualt value of the parameter in + /// question. + /// \return The value associated with then name in this parameter + /// group. + template + T getDefault(const std::string& name, + const T& default_value) const; + + template + T getDefault(const std::string& name, + const T& default_value, + const Requirement& r) const; + + /// \brief This method returns the parameter group given by name, + /// i.e. it is an alias of get(). + /// + /// \param name is the name of the parameter group sought. + /// \return the parameter group sought. + ParameterGroup getGroup(const std::string& name) const; + + /// \brief Disables the output from get, getDefault and getGroup. + /// By default, such output is enabled. + void disableOutput(); + + /// \brief Enables the output from get, getDefault and getGroup. + /// By default, such output is enabled. + void enableOutput(); + + /// \brief Returs true if and only if output from get, getDefault + /// and getGroup is enabled. + /// + /// \return true if and only if output from get, getDefault and + /// getGroup is enabled. + bool isOutputEnabled() const; + + + /// \brief Reads the contents of the xml file specified by + /// xml_filename into this ParameterGroup. + /// + /// \param xml_filename is the name of a xml file. + void readXML(const std::string& xml_filename); + + /// \brief Reads the contents of the param file specified by + /// param_filename into this ParameterGroup. + /// + /// NOTE: A param file contains lines on the form 'a/b/c=d'. + // The '/' separates ParameterGroups and Parameters + /// (e.g. c is a Parameter in the ParameterGroup b, + /// and b is a ParameterGroup in the ParameterGroup a) + /// while '=' separates the name from the value (e.g. the + /// value of the parameter c above will be d). + /// NOTE: A param file does not store any type information about + /// its values. + /// + /// \param param_filename is the name of a param file. + void readParam(const std::string& param_filename); + + /// \brief Writes this ParameterGroup into a param file + /// specified by param_filename. + /// + /// \param param_filename is the name of a param file. + void writeParam(const std::string& param_filename) const; + + /// \brief Writes this ParameterGroup to a stream. + /// + /// \param stream is the stream to write to. + void writeParamToStream(std::ostream& stream) const; + + + /// vki param interface - deprecated + template + void get(const char* name, T& value, const T& default_value) const { + value = this->getDefault(name, default_value); + } + + /// vki param interface - deprecated + template + void get(const char* name, T& value) const { + value = this->get(name); + } + + /// \brief Return true if any parameters are unused. + bool anyUnused() const; + + /// \brief Shows which parameters which are used or unused. + void displayUsage(bool used_params = false) const; + + /// \brief Returns the path of the parameter group. + std::string path() const; + + /// Insert a new item into the group. + void insert(const std::string& name, + const std::tr1::shared_ptr& data); + + /// Insert a new parameter item into the group. + void insertParameter(const std::string& name, const std::string& value); + + private: + typedef std::tr1::shared_ptr data_type; + typedef std::pair pair_type; + typedef std::map map_type; + + std::string path_; + map_type map_; + const ParameterGroup* parent_; + bool output_is_enabled_; + + template + T translate(const pair_type& data, const Requirement& chk) const; + template + void parseCommandLineArguments(int argc, StringArray argv); + void recursiveSetIsOutputEnabled(bool output_is_enabled); + }; + } // namespace parameter +} // namespace Dune + +#include + +#endif // OPENRS_PARAMETERGROUP_HEADER diff --git a/opm/core/utility/parameters/ParameterGroup_impl.hpp b/opm/core/utility/parameters/ParameterGroup_impl.hpp new file mode 100644 index 00000000..3ef77a72 --- /dev/null +++ b/opm/core/utility/parameters/ParameterGroup_impl.hpp @@ -0,0 +1,330 @@ +//=========================================================================== +// +// File: ParameterGroup_impl.hpp +// +// Created: Tue Jun 2 19:06:46 2009 +// +// Author(s): Bård Skaflestad +// Atgeirr F Rasmussen +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* +Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. +Copyright 2009, 2010 Statoil ASA. + +This file is part of The Open Reservoir Simulator Project (OpenRS). + +OpenRS is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenRS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OpenRS. If not, see . +*/ + +#ifndef OPENRS_PARAMETERGROUP_IMPL_HEADER +#define OPENRS_PARAMETERGROUP_IMPL_HEADER + +#include +#include + +//#include "TermColors.hpp" // from utils + +#include +#include +#include + +namespace Dune { + namespace parameter { + + template<> + struct ParameterMapItemTrait { + static ParameterGroup + convert(const ParameterMapItem& item, + std::string& conversion_error) + { + std::string tag = item.getTag(); + if (tag != ID_xmltag__param_grp) { + conversion_error = "The XML tag was '" + tag + + "' but should be '" + + ID_xmltag__param_grp + "'.\n"; + return ParameterGroup("", 0); + } + conversion_error = ""; + const ParameterGroup& pg = dynamic_cast(item); + return pg; + } + static std::string type() {return "ParameterGroup";} + }; + + namespace { + template + inline std::string + to_string(const T& val) + { + std::ostringstream os; + os << val; + return os.str(); + } + + inline std::string + to_string(const bool b) { + if (b) { + return ID_true; + } else { + return ID_false; + } + } + + inline std::string + to_string(const ParameterGroup&) + { + return std::string(""); + } + + std::pair + filename_split(const std::string& filename) + { + int fpos = filename.rfind('.'); + std::string name = filename.substr(0, fpos); + std::string type = filename.substr(fpos+1); + return std::make_pair(name, type); + } + + } + + + + template + ParameterGroup::ParameterGroup(int argc, StringArray argv) + : path_(ID_path_root), parent_(0), output_is_enabled_(true) + { + if (argc < 2) { + std::cerr << "Usage: " << argv[0] << " " + << "[paramfilename1.{xml,param}] " + << "[paramfilename2.{xml,param}] " + << "[overridden_arg1=value1] " + << "[overridden_arg2=value2] " + << "[...]" << std::endl; + exit(EXIT_FAILURE); + } + this->parseCommandLineArguments(argc, argv); + } + + template + void ParameterGroup::parseCommandLineArguments(int argc, StringArray argv) + { + std::vector files; + std::vector > assignments; + for (int i = 1; i < argc; ++i) { + std::string arg(argv[i]); + int fpos = arg.find(ID_delimiter_assignment); + if (fpos == int(std::string::npos)) { + std::string filename = arg.substr(0, fpos); + files.push_back(filename); + continue; + } + int pos = fpos + ID_delimiter_assignment.size(); + int spos = arg.find(ID_delimiter_assignment, pos); + if (spos == int(std::string::npos)) { + std::string name = arg.substr(0, fpos); + std::string value = arg.substr(pos, spos); + assignments.push_back(std::make_pair(name, value)); + continue; + } + std::cout << "WARNING: Too many assignements (' " + << ID_delimiter_assignment + << "') detected in argument " << i << ".\n"; + } + for (int i = 0; i < int(files.size()); ++i) { + std::pair file_type = filename_split(files[i]); + if (file_type.second == "xml") { + this->readXML(files[i]); + } else if (file_type.second == "param") { + this->readParam(files[i]); + } else { + std::cout << "WARNING: Ignoring file '" + << files[i] << "' with unknown extension.\n" + << "Valid filename extensions are 'xml' and 'param'.\n"; + } + } + for (int i = 0; i < int(assignments.size()); ++i) { + this->insertParameter(assignments[i].first, assignments[i].second); + } + } + + + template + inline T ParameterGroup::get(const std::string& name) const + { + return this->get(name, ParameterRequirementNone()); + } + + template + inline T ParameterGroup::get(const std::string& name, + const Requirement& r) const + { + setUsed(); + std::pair name_path = split(name); + map_type::const_iterator it = map_.find(name_path.first); + if (it == map_.end()) { + if (parent_ != 0) { + // If we have a parent, ask it instead. + if (output_is_enabled_) { + //TermColors::Red(); + std::cout << name; + //TermColors::Normal(); + std::cout << " not found at " + << (path() + ID_delimiter_path) + << ", asking parent." << std::endl; + } + return parent_->get(name, r); + } else { + // We are at the top, name has not been found. + std::cerr << "ERROR: The group '" + << this->path() + << "' does not contain an element named '" + << name + << "'.\n"; + throw NotFoundException(); + } + } + if (name_path.second == "") { + T val = this->translate(*it, r); + it->second->setUsed(); + if (output_is_enabled_) { + //TermColors::Green(); + std::cout << name; + //TermColors::Normal(); + std::cout << " found at " << (path() + ID_delimiter_path) + << ", value is " << to_string(val) << std::endl; + } + return val; + } else { + ParameterGroup& pg = dynamic_cast(*(*it).second); + pg.setUsed(); + return pg.get(name_path.second, r); + } + } + + template + inline T ParameterGroup::getDefault(const std::string& name, + const T& default_value) const + { + return this->getDefault(name, default_value, ParameterRequirementNone()); + } + + template + inline T ParameterGroup::getDefault(const std::string& name, + const T& default_value, + const Requirement& r) const + { + setUsed(); + std::pair name_path = split(name); + map_type::const_iterator it = map_.find(name_path.first); + if (it == map_.end()) { + if (parent_ != 0) { + // If we have a parent, ask it instead. + if (output_is_enabled_) { + //TermColors::Red(); + std::cout << name; + //TermColors::Normal(); + std::cout << " not found at " << (path() + ID_delimiter_path) + << ", asking parent." << std::endl; + } + return parent_->getDefault(name, default_value, r); + } else { + // We check the requirement for the default value + std::string requirement_result = r(default_value); + if (requirement_result != "") { + std::cerr << "ERROR: The default value for the " + << " element named '" + << name + << "' in the group '" + << this->path() + << "' failed to meet a requirenemt.\n"; + std::cerr << "The requirement enforcer returned the following message:\n" + << requirement_result + << "\n"; + throw RequirementFailedException(); + } + } + if (output_is_enabled_) { + //TermColors::Blue(); + std::cout << name; + //TermColors::Normal(); + std::cout << " not found. Using default value '" + << to_string(default_value) << "'." << std::endl; + } + return default_value; + } + if (name_path.second == "") { + T val = this->translate(*it, r); + it->second->setUsed(); + if (output_is_enabled_) { + //TermColors::Green(); + std::cout << name; + //TermColors::Normal(); + std::cout << " found at " << (path() + ID_delimiter_path) + << ", value is '" << to_string(val) << "'." << std::endl; + } + return val; + } else { + ParameterGroup& pg = dynamic_cast(*(*it).second); + pg.setUsed(); + return pg.getDefault(name_path.second, default_value, r); + } + } + + template + inline T ParameterGroup::translate(const pair_type& named_data, + const Requirement& chk) const + { + const std::string& name = named_data.first; + const data_type data = named_data.second; + std::string conversion_error; + T value = ParameterMapItemTrait::convert(*data, conversion_error); + if (conversion_error != "") { + std::cerr << "ERROR: Failed to convert the element named '" + << name + << "' in the group '" + << this->path() + << "' to the type '" + << ParameterMapItemTrait::type() + << "'.\n"; + std::cerr << "The conversion routine returned the following message:\n" + << conversion_error + << "\n"; + throw WrongTypeException(); + } + std::string requirement_result = chk(value); + if (requirement_result != "") { + std::cerr << "ERROR: The element named '" + << name + << "' in the group '" + << this->path() + << "' of type '" + << ParameterMapItemTrait::type() + << "' failed to meet a requirenemt.\n"; + std::cerr << "The requirement enforcer returned the following message:\n" + << requirement_result + << "\n"; + throw RequirementFailedException(); + } + return value; + } + } // namespace parameter +} // namespace Dune + +#endif // OPENRS_PARAMETERGROUP_IMPL_HEADER diff --git a/opm/core/utility/parameters/ParameterMapItem.hpp b/opm/core/utility/parameters/ParameterMapItem.hpp new file mode 100644 index 00000000..24248e61 --- /dev/null +++ b/opm/core/utility/parameters/ParameterMapItem.hpp @@ -0,0 +1,73 @@ +//=========================================================================== +// +// File: ParameterMapItem.hpp +// +// Created: Tue Jun 2 19:05:54 2009 +// +// Author(s): Bård Skaflestad +// Atgeirr F Rasmussen +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* +Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. +Copyright 2009, 2010 Statoil ASA. + +This file is part of The Open Reservoir Simulator Project (OpenRS). + +OpenRS is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenRS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OpenRS. If not, see . +*/ + +#ifndef OPENRS_PARAMETERMAPITEM_HEADER +#define OPENRS_PARAMETERMAPITEM_HEADER + +#include + +namespace Dune { + namespace parameter { + /// The parameter handlig system is structured as a tree, + /// where each node inhertis from ParameterMapItem. + /// + /// The abstract virtual function getTag() is used to determine + /// which derived class the node actually is. + struct ParameterMapItem { + /// Default constructor + ParameterMapItem() : used_(false) {} + + /// Destructor + virtual ~ParameterMapItem() {} + + /// \brief This function returns a string describing + /// the ParameterMapItem. + virtual std::string getTag() const = 0; + void setUsed() const { used_ = true; } + bool used() const { return used_; } + private: + mutable bool used_; + }; + + template + struct ParameterMapItemTrait { + static T convert(const ParameterMapItem&, + std::string& conversion_error); + static std::string type(); + }; + } // namespace parameter +} // namespace Dune + +#endif // OPENRS_PARAMETERMAPITEM_HEADER diff --git a/opm/core/utility/parameters/ParameterRequirement.hpp b/opm/core/utility/parameters/ParameterRequirement.hpp new file mode 100644 index 00000000..70d12a38 --- /dev/null +++ b/opm/core/utility/parameters/ParameterRequirement.hpp @@ -0,0 +1,243 @@ +//=========================================================================== +// +// File: ParameterRequirement.hpp +// +// Created: Tue Jun 2 19:05:02 2009 +// +// Author(s): Bård Skaflestad +// Atgeirr F Rasmussen +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* +Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. +Copyright 2009, 2010 Statoil ASA. + +This file is part of The Open Reservoir Simulator Project (OpenRS). + +OpenRS is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenRS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OpenRS. If not, see . +*/ + +#ifndef OPENRS_PARAMETERREQUIREMENT_HEADER +#define OPENRS_PARAMETERREQUIREMENT_HEADER + +#include +#include +#include +#include +#include + +namespace Dune { + namespace parameter { + /// @brief + /// @todo Doc me! + /// @tparam + /// @param + /// @return + struct ParameterRequirementNone { + template + std::string operator()(const T&) const { + return ""; + } + }; + + /// @brief + /// @todo Doc me! + /// @param + /// @return + struct ParameterRequirementProbability { + std::string operator()(double x) const { + if ( (x < 0.0) || (x > 1.0) ) { + std::ostringstream stream; + stream << "The value '" << x + << "' is not in the interval [0, 1]."; + return stream.str(); + } else { + return ""; + } + } + }; + + /// @brief + /// @todo Doc me! + /// @tparam + /// @param + /// @return + struct ParameterRequirementPositive { + template + std::string operator()(const T& x) const { + if (x > 0) { + return ""; + } else { + std::ostringstream stream; + stream << "The value '" << x << "' is not positive."; + return stream.str(); + } + } + }; + + /// @brief + /// @todo Doc me! + /// @tparam + /// @param + /// @return + struct ParameterRequirementNegative { + template + std::string operator()(const T& x) const { + if (x < 0) { + return ""; + } else { + std::ostringstream stream; + stream << "The value '" << x << "' is not negative."; + return stream.str(); + } + } + }; + + /// @brief + /// @todo Doc me! + /// @tparam + /// @param + /// @return + struct ParameterRequirementNonPositive { + template + std::string operator()(const T& x) const { + if (x > 0) { + std::ostringstream stream; + stream << "The value '" << x << "' is positive."; + return stream.str(); + } else { + return ""; + } + } + }; + + /// @brief + /// @todo Doc me! + /// @tparam + /// @param + /// @return + struct ParameterRequirementNonNegative { + template + std::string operator()(const T& x) const { + if (x < 0) { + std::ostringstream stream; + stream << "The value '" << x << "' is negative."; + return stream.str(); + } else { + return ""; + } + } + }; + + /// @brief + /// @todo Doc me! + /// @tparam + /// @param + /// @return + struct ParameterRequirementNonZero { + template + std::string operator()(const T& x) const { + if (x != 0) { + return ""; + } else { + return "The value was zero."; + } + } + }; + + /// @brief + /// @todo Doc me! + /// @param + /// @return + struct ParameterRequirementNonEmpty { + std::string operator()(const std::string& x) const { + if (x != "") { + return "The string was empty."; + } else { + return ""; + } + } + }; + + /// @brief + /// @todo Doc me! + /// @tparam + /// @param + /// @return + template + struct ParameterRequirementAnd { + ParameterRequirementAnd(const Requirement1& r1, const Requirement2& r2) : + r1_(r1), r2_(r2) { } + + template + std::string operator()(const T& t) const { + std::string e1 = r1_(t); + std::string e2 = r2_(t); + if (e1 == "") { + return e2; + } else if (e2 == "") { + return e1; + } else { + return e1 + " AND " + e2; + } + } + private: + const Requirement1 r1_; + const Requirement2 r2_; + }; + + /// @brief + /// @todo Doc me! + /// @param + struct ParameterRequirementMemberOf { + ParameterRequirementMemberOf(const std::vector& elements) + : elements_(elements) { + assert(elements_.size() > 0); + } + + /// @brief + /// @todo Doc me! + /// @param + /// @return + std::string operator()(const std::string& x) const { + if (std::find(elements_.begin(), elements_.end(), x) == elements_.end()) { + if (elements_.size() == 1) { + return "The string '" + x + "' is not '" + elements_[0] + "'."; + } + std::ostringstream stream; + stream << "The string '" << x << "' is not among '"; + for (int i = 0; i < int(elements_.size()) - 2; ++i) { + stream << elements_[i] << "', '"; + } + stream << elements_[elements_.size() - 2] + << "' and '" + << elements_[elements_.size() - 1] + << "'."; + return stream.str(); + } else { + return ""; + } + } + private: + const std::vector elements_; + }; + } // namespace parameter +} // namespace Dune + +#endif // OPENRS_PARAMETERREQUIREMENT_HEADER diff --git a/opm/core/utility/parameters/ParameterStrings.hpp b/opm/core/utility/parameters/ParameterStrings.hpp new file mode 100644 index 00000000..2b61fdb6 --- /dev/null +++ b/opm/core/utility/parameters/ParameterStrings.hpp @@ -0,0 +1,70 @@ +//=========================================================================== +// +// File: ParameterStrings.hpp +// +// Created: Tue Jun 2 19:04:15 2009 +// +// Author(s): Bård Skaflestad +// Atgeirr F Rasmussen +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* +Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. +Copyright 2009, 2010 Statoil ASA. + +This file is part of The Open Reservoir Simulator Project (OpenRS). + +OpenRS is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenRS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OpenRS. If not, see . +*/ + +#ifndef OPENRS_PARAMETERSTRINGS_HEADER +#define OPENRS_PARAMETERSTRINGS_HEADER + +#include + +namespace Dune { + namespace parameter { + const std::string ID_true = "true"; + const std::string ID_false = "false"; + + const std::string ID_xmltag__param_grp = "ParameterGroup"; + const std::string ID_xmltag__param = "Parameter"; + const std::string ID_xmltag__file_param_grp = "ParameterGroupFromFile"; + const std::string ID_xmltag__file_params = "MergeWithFile"; + + const std::string ID_xmlatt__value = "value"; + const std::string ID_xmlatt__name = "name"; + const std::string ID_xmlatt__type = "type"; + + const std::string ID_param_type__bool = "bool"; + const std::string ID_param_type__int = "int"; + const std::string ID_param_type__float = "double"; + const std::string ID_param_type__string = "string"; + const std::string ID_param_type__file = "file"; + const std::string ID_param_type__cmdline = "cmdline"; + + // + + const std::string ID_path_root = ""; + const std::string ID_delimiter_path = "/"; + const std::string ID_delimiter_assignment = "="; + } // namespace parameter +} // namespace Dune + +#endif // OPENRS_PARAMETERSTRINGS_HEADER diff --git a/opm/core/utility/parameters/ParameterTools.cpp b/opm/core/utility/parameters/ParameterTools.cpp new file mode 100644 index 00000000..f034be71 --- /dev/null +++ b/opm/core/utility/parameters/ParameterTools.cpp @@ -0,0 +1,55 @@ +//=========================================================================== +// +// File: ParameterTools.cpp +// +// Created: Tue Jun 2 19:03:09 2009 +// +// Author(s): Bård Skaflestad +// Atgeirr F Rasmussen +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* +Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. +Copyright 2009, 2010 Statoil ASA. + +This file is part of The Open Reservoir Simulator Project (OpenRS). + +OpenRS is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenRS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OpenRS. If not, see . +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include + +namespace Dune { + namespace parameter { + std::pair split(const std::string& name) + { + int pos = name.find(ID_delimiter_path); + if (pos == int(std::string::npos)) { + return std::make_pair(name, ""); + } else { + return std::make_pair(name.substr(0, pos), + name.substr(pos + ID_delimiter_path.size())); + } + } + } // namespace parameter +} // namespace Dune diff --git a/opm/core/utility/parameters/ParameterTools.hpp b/opm/core/utility/parameters/ParameterTools.hpp new file mode 100644 index 00000000..05701f96 --- /dev/null +++ b/opm/core/utility/parameters/ParameterTools.hpp @@ -0,0 +1,48 @@ +//=========================================================================== +// +// File: ParameterTools.hpp +// +// Created: Tue Jun 2 19:02:19 2009 +// +// Author(s): Bård Skaflestad +// Atgeirr F Rasmussen +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* +Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. +Copyright 2009, 2010 Statoil ASA. + +This file is part of The Open Reservoir Simulator Project (OpenRS). + +OpenRS is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenRS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OpenRS. If not, see . +*/ + +#ifndef OPENRS_PARAMETERTOOLS_HEADER +#define OPENRS_PARAMETERTOOLS_HEADER + +#include +#include + +namespace Dune { + namespace parameter { + std::pair split(const std::string& name); + } // namespace parameter +} // namespace Dune + +#endif // OPENRS_PARAMETERTOOLS_HEADER diff --git a/opm/core/utility/parameters/ParameterXML.cpp b/opm/core/utility/parameters/ParameterXML.cpp new file mode 100644 index 00000000..4e69a0ac --- /dev/null +++ b/opm/core/utility/parameters/ParameterXML.cpp @@ -0,0 +1,136 @@ +//=========================================================================== +// +// File: ParameterXML.cpp +// +// Created: Tue Jun 2 18:57:25 2009 +// +// Author(s): Bård Skaflestad +// Atgeirr F Rasmussen +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* +Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. +Copyright 2009, 2010 Statoil ASA. + +This file is part of The Open Reservoir Simulator Project (OpenRS). + +OpenRS is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenRS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OpenRS. If not, see . +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include + +#include +#include +#include +#include + + +#include + + + +namespace Dune { + namespace parameter { + + namespace tinyxml { + void read_xml(ParameterGroup& pg, const std::string filename); + void fill_tree(ParameterGroup& pg, + const TiXmlNode* root, + const std::string& xml_file_path); + } + + void fill_xml(ParameterGroup& pg, const std::string filename) { + tinyxml::read_xml(pg, filename); + } + + namespace tinyxml { + std::string getProperty(const std::string& property, + const TiXmlElement* node_ptr) + { + const char* prop_value_ptr = node_ptr->Attribute(property.c_str()); + std::string property_value(prop_value_ptr ? prop_value_ptr : ""); + return property_value; + } + + void read_xml(ParameterGroup& pg, const std::string filename) + { + TiXmlDocument doc(filename); + bool ok = doc.LoadFile(); + if (!ok) { + std::cerr << "ERROR: Failed to open XML file '" << filename << "'\n"; + throw std::exception(); + } + const TiXmlNode* root = doc.RootElement(); + std::string xml_file_path = boost::filesystem::path(filename).branch_path().string(); + fill_tree(pg, root, xml_file_path); + } + + void fill_tree(ParameterGroup& pg, + const TiXmlNode* root, + const std::string& xml_file_path) + { + //std::cout << "GROUP '" << value << "' BEGIN\n"; + for (const TiXmlNode* cur = root->FirstChild(); cur; cur = cur->NextSibling()) { + const TiXmlElement* elem = cur->ToElement(); + if (elem) { + std::string tag_name = elem->ValueStr(); + if (tag_name == ID_xmltag__file_params) { + std::string relative_filename = getProperty(ID_xmlatt__value, elem); + std::string filename = (boost::filesystem::path(xml_file_path) / relative_filename).string(); + fill_xml(pg, filename); + continue; + } + std::string name = getProperty(ID_xmlatt__name, elem); + std::tr1::shared_ptr data; + if (tag_name == ID_xmltag__param) { + std::string value = getProperty(ID_xmlatt__value, elem); + std::string type = getProperty(ID_xmlatt__type, elem); + if (type == ID_param_type__file) { + value = (boost::filesystem::path(xml_file_path) / value).string(); + type = ID_param_type__string; + } + data.reset(new Parameter(value, type)); + } else if (tag_name == ID_xmltag__param_grp) { + std::string child_path = pg.path() + ID_delimiter_path + name; + data.reset(new ParameterGroup(child_path, &pg)); + fill_tree(dynamic_cast(*data), elem, xml_file_path); + } else if (tag_name == ID_xmltag__file_param_grp) { + std::string child_path = pg.path() + ID_delimiter_path + name; + data.reset(new ParameterGroup(child_path, &pg)); + std::string relative_filename = getProperty(ID_xmlatt__value, elem); + std::string filename = (boost::filesystem::path(xml_file_path) / relative_filename).string(); + fill_xml(dynamic_cast(*data), filename); + } else { + std::cerr << "ERROR: '" << tag_name << "' is an unknown xml tag.\n"; + throw std::exception(); + } + pg.insert(name, data); + } + } + //std::cout << "GROUP '" << id << "' END\n"; + } + } + } // namespace parameter +} // namespace Dune + diff --git a/opm/core/utility/parameters/ParameterXML.hpp b/opm/core/utility/parameters/ParameterXML.hpp new file mode 100644 index 00000000..0e5b58dd --- /dev/null +++ b/opm/core/utility/parameters/ParameterXML.hpp @@ -0,0 +1,54 @@ +//=========================================================================== +// +// File: ParameterXML.hpp +// +// Created: Tue Jun 2 18:55:59 2009 +// +// Author(s): Bård Skaflestad +// Atgeirr F Rasmussen +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* +Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. +Copyright 2009, 2010 Statoil ASA. + +This file is part of The Open Reservoir Simulator Project (OpenRS). + +OpenRS is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OpenRS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with OpenRS. If not, see . +*/ + +#ifndef OPENRS_PARAMETERXML_HEADER +#define OPENRS_PARAMETERXML_HEADER + +#include + +namespace Dune { + namespace parameter { + /// \brief This function fills a ParameterGroup with a XML file. + /// + /// NOTE: The function throws if there is an error during reading + /// of the XML file. + /// + /// \retval pg is the ParameterGroup to be filled. + /// \param filename is the name of an XML file. + void fill_xml(ParameterGroup& pg, const std::string filename); + } // namespace parameter +} // namespace Dune + +#endif // OPENRS_PARAMETERXML_HEADER diff --git a/opm/core/utility/parameters/test/Makefile.am b/opm/core/utility/parameters/test/Makefile.am new file mode 100644 index 00000000..7a561dc9 --- /dev/null +++ b/opm/core/utility/parameters/test/Makefile.am @@ -0,0 +1,14 @@ +# $Date: 2009-12-08 15:21:36 +0100 (Tue, 08 Dec 2009) $ +# $Revision: 699 $ + +check_PROGRAMS = param_test + +param_test_SOURCES = param_test.cpp +param_test_CXXFLAGS = $(DUNEMPICPPFLAGS) $(BOOST_CPPFLAGS) +param_test_LDADD = $(DUNE_LDFLAGS) $(DUNEMPILDFLAGS) $(DUNEMPILIBS) $(BOOST_LDFLAGS) \ + $(DUNE_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) ../libparam.la + +TESTS = $(check_PROGRAMS) + +include $(top_srcdir)/am/global-rules + diff --git a/opm/core/utility/parameters/test/extratestdata.xml b/opm/core/utility/parameters/test/extratestdata.xml new file mode 100644 index 00000000..de6ab123 --- /dev/null +++ b/opm/core/utility/parameters/test/extratestdata.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/opm/core/utility/parameters/test/param_test.cpp b/opm/core/utility/parameters/test/param_test.cpp new file mode 100644 index 00000000..2765ead8 --- /dev/null +++ b/opm/core/utility/parameters/test/param_test.cpp @@ -0,0 +1,101 @@ +//=========================================================================== +// +// File: param_test.cpp +// +// Created: Sun Dec 13 20:08:36 2009 +// +// Author(s): Atgeirr F Rasmussen +// Bård Skaflestad +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* + Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. + Copyright 2009, 2010 Statoil ASA. + + This file is part of The Open Reservoir Simulator Project (OpenRS). + + OpenRS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenRS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenRS. If not, see . +*/ + + +#define BOOST_TEST_DYN_LINK +#define NVERBOSE // to suppress our messages when throwing + +#define BOOST_TEST_MODULE ParameterTest +#include + +#include "../ParameterGroup.hpp" +#include +#include + +using namespace Dune; + +BOOST_AUTO_TEST_CASE(commandline_syntax_init) +{ + typedef const char* cp; + cp argv[] = { "program_command", + "topitem=somestring", + "/slashtopitem=anotherstring", + "/group/item=1", + "/group/anotheritem=2", + "/group/subgroup/item=3", + "/group/subgroup/anotheritem=4", + "/group/item=overridingstring" }; + const std::size_t argc = sizeof(argv)/sizeof(argv[0]); + parameter::ParameterGroup p(argc, argv); + BOOST_CHECK(p.get("topitem") == "somestring"); + std::ostringstream os; + p.writeParamToStream(os); + std::string correct_answer = "/group/anotheritem=2\n" + "/group/item=overridingstring\n" + "/group/subgroup/anotheritem=4\n" + "/group/subgroup/item=3\n" + "/slashtopitem=anotherstring\n" + "/topitem=somestring\n"; + BOOST_CHECK(os.str() == correct_answer); + + // Tests that only run in debug mode. +#ifndef NDEBUG +#endif +} + + +BOOST_AUTO_TEST_CASE(xml_syntax_init) +{ + typedef const char* cp; + cp argv[] = { "program_command", + "testdata.xml", + "/group/item=overridingstring" }; + const std::size_t argc = sizeof(argv)/sizeof(argv[0]); + parameter::ParameterGroup p(argc, argv); + BOOST_CHECK(p.get("topitem") == "somestring"); + std::ostringstream os; + p.writeParamToStream(os); + std::string correct_answer = "/group/anotheritem=2\n" + "/group/item=overridingstring\n" + "/group/subgroup/anotheritem=4\n" + "/group/subgroup/item=3\n" + "/slashtopitem=anotherstring\n" + "/topitem=somestring\n"; + BOOST_CHECK(os.str() == correct_answer); + + // Tests that only run in debug mode. +#ifndef NDEBUG +#endif +} diff --git a/opm/core/utility/parameters/test/testdata.xml b/opm/core/utility/parameters/test/testdata.xml new file mode 100644 index 00000000..e108302a --- /dev/null +++ b/opm/core/utility/parameters/test/testdata.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/opm/core/utility/parameters/tinyxml/Makefile.am b/opm/core/utility/parameters/tinyxml/Makefile.am new file mode 100644 index 00000000..2f4476f7 --- /dev/null +++ b/opm/core/utility/parameters/tinyxml/Makefile.am @@ -0,0 +1,10 @@ +# $Date$ +# $Revision$ + +SUBDIRS = + +noinst_LTLIBRARIES = libtinyxml.la + +libtinyxml_la_SOURCES = tinystr.cpp tinyxml.cpp tinyxmlerror.cpp tinyxmlparser.cpp + +include $(top_srcdir)/am/global-rules diff --git a/opm/core/utility/parameters/tinyxml/tinystr.cpp b/opm/core/utility/parameters/tinyxml/tinystr.cpp new file mode 100644 index 00000000..06657682 --- /dev/null +++ b/opm/core/utility/parameters/tinyxml/tinystr.cpp @@ -0,0 +1,111 @@ +/* +www.sourceforge.net/projects/tinyxml + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TIXML_USE_STL + +#include "tinystr.h" + +// Error value for find primitive +const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1); + + +// Null rep. +TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } }; + + +void TiXmlString::reserve (size_type cap) +{ + if (cap > capacity()) + { + TiXmlString tmp; + tmp.init(length(), cap); + memcpy(tmp.start(), data(), length()); + swap(tmp); + } +} + + +TiXmlString& TiXmlString::assign(const char* str, size_type len) +{ + size_type cap = capacity(); + if (len > cap || cap > 3*(len + 8)) + { + TiXmlString tmp; + tmp.init(len); + memcpy(tmp.start(), str, len); + swap(tmp); + } + else + { + memmove(start(), str, len); + set_size(len); + } + return *this; +} + + +TiXmlString& TiXmlString::append(const char* str, size_type len) +{ + size_type newsize = length() + len; + if (newsize > capacity()) + { + reserve (newsize + capacity()); + } + memmove(finish(), str, len); + set_size(newsize); + return *this; +} + + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b) +{ + TiXmlString tmp; + tmp.reserve(a.length() + b.length()); + tmp += a; + tmp += b; + return tmp; +} + +TiXmlString operator + (const TiXmlString & a, const char* b) +{ + TiXmlString tmp; + TiXmlString::size_type b_len = static_cast( strlen(b) ); + tmp.reserve(a.length() + b_len); + tmp += a; + tmp.append(b, b_len); + return tmp; +} + +TiXmlString operator + (const char* a, const TiXmlString & b) +{ + TiXmlString tmp; + TiXmlString::size_type a_len = static_cast( strlen(a) ); + tmp.reserve(a_len + b.length()); + tmp.append(a, a_len); + tmp += b; + return tmp; +} + + +#endif // TIXML_USE_STL diff --git a/opm/core/utility/parameters/tinyxml/tinystr.h b/opm/core/utility/parameters/tinyxml/tinystr.h new file mode 100644 index 00000000..89cca334 --- /dev/null +++ b/opm/core/utility/parameters/tinyxml/tinystr.h @@ -0,0 +1,305 @@ +/* +www.sourceforge.net/projects/tinyxml + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TIXML_USE_STL + +#ifndef TIXML_STRING_INCLUDED +#define TIXML_STRING_INCLUDED + +#include +#include + +/* The support for explicit isn't that universal, and it isn't really + required - it is used to check that the TiXmlString class isn't incorrectly + used. Be nice to old compilers and macro it here: +*/ +#if defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + #define TIXML_EXPLICIT explicit +#elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + #define TIXML_EXPLICIT explicit +#else + #define TIXML_EXPLICIT +#endif + + +/* + TiXmlString is an emulation of a subset of the std::string template. + Its purpose is to allow compiling TinyXML on compilers with no or poor STL support. + Only the member functions relevant to the TinyXML project have been implemented. + The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase + a string and there's no more room, we allocate a buffer twice as big as we need. +*/ +class TiXmlString +{ + public : + // The size type used + typedef size_t size_type; + + // Error value for find primitive + static const size_type npos; // = -1; + + + // TiXmlString empty constructor + TiXmlString () : rep_(&nullrep_) + { + } + + // TiXmlString copy constructor + TiXmlString ( const TiXmlString & copy) : rep_(0) + { + init(copy.length()); + memcpy(start(), copy.data(), length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0) + { + init( static_cast( strlen(copy) )); + memcpy(start(), copy, length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0) + { + init(len); + memcpy(start(), str, len); + } + + // TiXmlString destructor + ~TiXmlString () + { + quit(); + } + + TiXmlString& operator = (const char * copy) + { + return assign( copy, (size_type)strlen(copy)); + } + + TiXmlString& operator = (const TiXmlString & copy) + { + return assign(copy.start(), copy.length()); + } + + + // += operator. Maps to append + TiXmlString& operator += (const char * suffix) + { + return append(suffix, static_cast( strlen(suffix) )); + } + + // += operator. Maps to append + TiXmlString& operator += (char single) + { + return append(&single, 1); + } + + // += operator. Maps to append + TiXmlString& operator += (const TiXmlString & suffix) + { + return append(suffix.data(), suffix.length()); + } + + + // Convert a TiXmlString into a null-terminated char * + const char * c_str () const { return rep_->str; } + + // Convert a TiXmlString into a char * (need not be null terminated). + const char * data () const { return rep_->str; } + + // Return the length of a TiXmlString + size_type length () const { return rep_->size; } + + // Alias for length() + size_type size () const { return rep_->size; } + + // Checks if a TiXmlString is empty + bool empty () const { return rep_->size == 0; } + + // Return capacity of string + size_type capacity () const { return rep_->capacity; } + + + // single char extraction + const char& at (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // [] operator + char& operator [] (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // find a char in a string. Return TiXmlString::npos if not found + size_type find (char lookup) const + { + return find(lookup, 0); + } + + // find a char in a string from an offset. Return TiXmlString::npos if not found + size_type find (char tofind, size_type offset) const + { + if (offset >= length()) return npos; + + for (const char* p = c_str() + offset; *p != '\0'; ++p) + { + if (*p == tofind) return static_cast< size_type >( p - c_str() ); + } + return npos; + } + + void clear () + { + //Lee: + //The original was just too strange, though correct: + // TiXmlString().swap(*this); + //Instead use the quit & re-init: + quit(); + init(0,0); + } + + /* Function to reserve a big amount of data when we know we'll need it. Be aware that this + function DOES NOT clear the content of the TiXmlString if any exists. + */ + void reserve (size_type cap); + + TiXmlString& assign (const char* str, size_type len); + + TiXmlString& append (const char* str, size_type len); + + void swap (TiXmlString& other) + { + Rep* r = rep_; + rep_ = other.rep_; + other.rep_ = r; + } + + private: + + void init(size_type sz) { init(sz, sz); } + void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; } + char* start() const { return rep_->str; } + char* finish() const { return rep_->str + rep_->size; } + + struct Rep + { + size_type size, capacity; + char str[1]; + }; + + void init(size_type sz, size_type cap) + { + if (cap) + { + // Lee: the original form: + // rep_ = static_cast(operator new(sizeof(Rep) + cap)); + // doesn't work in some cases of new being overloaded. Switching + // to the normal allocation, although use an 'int' for systems + // that are overly picky about structure alignment. + const size_type bytesNeeded = sizeof(Rep) + cap; + const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int ); + rep_ = reinterpret_cast( new int[ intsNeeded ] ); + + rep_->str[ rep_->size = sz ] = '\0'; + rep_->capacity = cap; + } + else + { + rep_ = &nullrep_; + } + } + + void quit() + { + if (rep_ != &nullrep_) + { + // The rep_ is really an array of ints. (see the allocator, above). + // Cast it back before delete, so the compiler won't incorrectly call destructors. + delete [] ( reinterpret_cast( rep_ ) ); + } + } + + Rep * rep_; + static Rep nullrep_; + +} ; + + +inline bool operator == (const TiXmlString & a, const TiXmlString & b) +{ + return ( a.length() == b.length() ) // optimization on some platforms + && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare +} +inline bool operator < (const TiXmlString & a, const TiXmlString & b) +{ + return strcmp(a.c_str(), b.c_str()) < 0; +} + +inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); } +inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; } +inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); } +inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); } + +inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; } +inline bool operator == (const char* a, const TiXmlString & b) { return b == a; } +inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); } +inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); } + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b); +TiXmlString operator + (const TiXmlString & a, const char* b); +TiXmlString operator + (const char* a, const TiXmlString & b); + + +/* + TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString. + Only the operators that we need for TinyXML have been developped. +*/ +class TiXmlOutStream : public TiXmlString +{ +public : + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const TiXmlString & in) + { + *this += in; + return *this; + } + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const char * in) + { + *this += in; + return *this; + } + +} ; + +#endif // TIXML_STRING_INCLUDED +#endif // TIXML_USE_STL diff --git a/opm/core/utility/parameters/tinyxml/tinyxml.cpp b/opm/core/utility/parameters/tinyxml/tinyxml.cpp new file mode 100644 index 00000000..9c161dfc --- /dev/null +++ b/opm/core/utility/parameters/tinyxml/tinyxml.cpp @@ -0,0 +1,1886 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include + +#ifdef TIXML_USE_STL +#include +#include +#endif + +#include "tinyxml.h" + +FILE* TiXmlFOpen( const char* filename, const char* mode ); + +bool TiXmlBase::condenseWhiteSpace = true; + +// Microsoft compiler security +FILE* TiXmlFOpen( const char* filename, const char* mode ) +{ + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + FILE* fp = 0; + errno_t err = fopen_s( &fp, filename, mode ); + if ( !err && fp ) + return fp; + return 0; + #else + return fopen( filename, mode ); + #endif +} + +void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString ) +{ + int i=0; + + while( i<(int)str.length() ) + { + unsigned char c = (unsigned char) str[i]; + + if ( c == '&' + && i < ( (int)str.length() - 2 ) + && str[i+1] == '#' + && str[i+2] == 'x' ) + { + // Hexadecimal character reference. + // Pass through unchanged. + // © -- copyright symbol, for example. + // + // The -1 is a bug fix from Rob Laveaux. It keeps + // an overflow from happening if there is no ';'. + // There are actually 2 ways to exit this loop - + // while fails (error case) and break (semicolon found). + // However, there is no mechanism (currently) for + // this function to return an error. + while ( i<(int)str.length()-1 ) + { + outString->append( str.c_str() + i, 1 ); + ++i; + if ( str[i] == ';' ) + break; + } + } + else if ( c == '&' ) + { + outString->append( entity[0].str, entity[0].strLength ); + ++i; + } + else if ( c == '<' ) + { + outString->append( entity[1].str, entity[1].strLength ); + ++i; + } + else if ( c == '>' ) + { + outString->append( entity[2].str, entity[2].strLength ); + ++i; + } + else if ( c == '\"' ) + { + outString->append( entity[3].str, entity[3].strLength ); + ++i; + } + else if ( c == '\'' ) + { + outString->append( entity[4].str, entity[4].strLength ); + ++i; + } + else if ( c < 32 ) + { + // Easy pass at non-alpha/numeric/symbol + // Below 32 is symbolic. + char buf[ 32 ]; + + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) ); + #else + sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); + #endif + + //*ME: warning C4267: convert 'size_t' to 'int' + //*ME: Int-Cast to make compiler happy ... + outString->append( buf, (int)strlen( buf ) ); + ++i; + } + else + { + //char realc = (char) c; + //outString->append( &realc, 1 ); + *outString += (char) c; // somewhat more efficient function call. + ++i; + } + } +} + + +TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() +{ + parent = 0; + type = _type; + firstChild = 0; + lastChild = 0; + prev = 0; + next = 0; +} + + +TiXmlNode::~TiXmlNode() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } +} + + +void TiXmlNode::CopyTo( TiXmlNode* target ) const +{ + target->SetValue (value.c_str() ); + target->userData = userData; + target->location = location; +} + + +void TiXmlNode::Clear() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } + + firstChild = 0; + lastChild = 0; +} + + +TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) +{ + assert( node->parent == 0 || node->parent == this ); + assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() ); + + if ( node->Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + delete node; + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + node->parent = this; + + node->prev = lastChild; + node->next = 0; + + if ( lastChild ) + lastChild->next = node; + else + firstChild = node; // it was an empty list. + + lastChild = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) +{ + if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + + return LinkEndChild( node ); +} + + +TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) +{ + if ( !beforeThis || beforeThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->next = beforeThis; + node->prev = beforeThis->prev; + if ( beforeThis->prev ) + { + beforeThis->prev->next = node; + } + else + { + assert( firstChild == beforeThis ); + firstChild = node; + } + beforeThis->prev = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) +{ + if ( !afterThis || afterThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->prev = afterThis; + node->next = afterThis->next; + if ( afterThis->next ) + { + afterThis->next->prev = node; + } + else + { + assert( lastChild == afterThis ); + lastChild = node; + } + afterThis->next = node; + return node; +} + + +TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) +{ + if ( !replaceThis ) + return 0; + + if ( replaceThis->parent != this ) + return 0; + + if ( withThis.ToDocument() ) { + // A document can never be a child. Thanks to Noam. + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = withThis.Clone(); + if ( !node ) + return 0; + + node->next = replaceThis->next; + node->prev = replaceThis->prev; + + if ( replaceThis->next ) + replaceThis->next->prev = node; + else + lastChild = node; + + if ( replaceThis->prev ) + replaceThis->prev->next = node; + else + firstChild = node; + + delete replaceThis; + node->parent = this; + return node; +} + + +bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) +{ + if ( !removeThis ) { + return false; + } + + if ( removeThis->parent != this ) + { + assert( 0 ); + return false; + } + + if ( removeThis->next ) + removeThis->next->prev = removeThis->prev; + else + lastChild = removeThis->prev; + + if ( removeThis->prev ) + removeThis->prev->next = removeThis->next; + else + firstChild = removeThis->next; + + delete removeThis; + return true; +} + +const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = firstChild; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = lastChild; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild(); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling(); + } +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild( val ); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling( val ); + } +} + + +const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = next; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = prev; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +void TiXmlElement::RemoveAttribute( const char * name ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING str( name ); + TiXmlAttribute* node = attributeSet.Find( str ); + #else + TiXmlAttribute* node = attributeSet.Find( name ); + #endif + if ( node ) + { + attributeSet.Remove( node ); + delete node; + } +} + +const TiXmlElement* TiXmlNode::FirstChildElement() const +{ + const TiXmlNode* node; + + for ( node = FirstChild(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = FirstChild( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement() const +{ + const TiXmlNode* node; + + for ( node = NextSibling(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = NextSibling( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlDocument* TiXmlNode::GetDocument() const +{ + const TiXmlNode* node; + + for( node = this; node; node = node->parent ) + { + if ( node->ToDocument() ) + return node->ToDocument(); + } + return 0; +} + + +TiXmlElement::TiXmlElement (const char * _value) + : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} + + +#ifdef TIXML_USE_STL +TiXmlElement::TiXmlElement( const std::string& _value ) + : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} +#endif + + +TiXmlElement::TiXmlElement( const TiXmlElement& copy) + : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) +{ + firstChild = lastChild = 0; + copy.CopyTo( this ); +} + + +TiXmlElement& TiXmlElement::operator=( const TiXmlElement& base ) +{ + ClearThis(); + base.CopyTo( this ); + return *this; +} + + +TiXmlElement::~TiXmlElement() +{ + ClearThis(); +} + + +void TiXmlElement::ClearThis() +{ + Clear(); + while( attributeSet.First() ) + { + TiXmlAttribute* node = attributeSet.First(); + attributeSet.Remove( node ); + delete node; + } +} + + +const char* TiXmlElement::Attribute( const char* name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return node->Value(); + return 0; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( attrib ) + return &attrib->ValueStr(); + return 0; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, int* i ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const char* result = 0; + + if ( attrib ) { + result = attrib->Value(); + if ( i ) { + attrib->QueryIntValue( i ); + } + } + return result; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const std::string* result = 0; + + if ( attrib ) { + result = &attrib->ValueStr(); + if ( i ) { + attrib->QueryIntValue( i ); + } + } + return result; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, double* d ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const char* result = 0; + + if ( attrib ) { + result = attrib->Value(); + if ( d ) { + attrib->QueryDoubleValue( d ); + } + } + return result; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const std::string* result = 0; + + if ( attrib ) { + result = &attrib->ValueStr(); + if ( d ) { + attrib->QueryDoubleValue( d ); + } + } + return result; +} +#endif + + +int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryIntValue( ival ); +} + + +int TiXmlElement::QueryUnsignedAttribute( const char* name, unsigned* value ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + int ival = 0; + int result = node->QueryIntValue( &ival ); + *value = (unsigned)ival; + return result; +} + + +int TiXmlElement::QueryBoolAttribute( const char* name, bool* bval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + int result = TIXML_WRONG_TYPE; + if ( StringEqual( node->Value(), "true", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "yes", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "1", true, TIXML_ENCODING_UNKNOWN ) ) + { + *bval = true; + result = TIXML_SUCCESS; + } + else if ( StringEqual( node->Value(), "false", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "no", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "0", true, TIXML_ENCODING_UNKNOWN ) ) + { + *bval = false; + result = TIXML_SUCCESS; + } + return result; +} + + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryIntValue( ival ); +} +#endif + + +int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryDoubleValue( dval ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryDoubleValue( dval ); +} +#endif + + +void TiXmlElement::SetAttribute( const char * name, int val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetIntValue( val ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, int val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetIntValue( val ); + } +} +#endif + + +void TiXmlElement::SetDoubleAttribute( const char * name, double val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetDoubleValue( val ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetDoubleAttribute( const std::string& name, double val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetDoubleValue( val ); + } +} +#endif + + +void TiXmlElement::SetAttribute( const char * cname, const char * cvalue ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( cname ); + if ( attrib ) { + attrib->SetValue( cvalue ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& _name, const std::string& _value ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( _name ); + if ( attrib ) { + attrib->SetValue( _value ); + } +} +#endif + + +void TiXmlElement::Print( FILE* cfile, int depth ) const +{ + int i; + assert( cfile ); + for ( i=0; iNext() ) + { + fprintf( cfile, " " ); + attrib->Print( cfile, depth ); + } + + // There are 3 different formatting approaches: + // 1) An element without children is printed as a node + // 2) An element with only a text child is printed as text + // 3) An element with children is printed on multiple lines. + TiXmlNode* node; + if ( !firstChild ) + { + fprintf( cfile, " />" ); + } + else if ( firstChild == lastChild && firstChild->ToText() ) + { + fprintf( cfile, ">" ); + firstChild->Print( cfile, depth + 1 ); + fprintf( cfile, "", value.c_str() ); + } + else + { + fprintf( cfile, ">" ); + + for ( node = firstChild; node; node=node->NextSibling() ) + { + if ( !node->ToText() ) + { + fprintf( cfile, "\n" ); + } + node->Print( cfile, depth+1 ); + } + fprintf( cfile, "\n" ); + for( i=0; i", value.c_str() ); + } +} + + +void TiXmlElement::CopyTo( TiXmlElement* target ) const +{ + // superclass: + TiXmlNode::CopyTo( target ); + + // Element class: + // Clone the attributes, then clone the children. + const TiXmlAttribute* attribute = 0; + for( attribute = attributeSet.First(); + attribute; + attribute = attribute->Next() ) + { + target->SetAttribute( attribute->Name(), attribute->Value() ); + } + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + +bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this, attributeSet.First() ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +TiXmlNode* TiXmlElement::Clone() const +{ + TiXmlElement* clone = new TiXmlElement( Value() ); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +const char* TiXmlElement::GetText() const +{ + const TiXmlNode* child = this->FirstChild(); + if ( child ) { + const TiXmlText* childText = child->ToText(); + if ( childText ) { + return childText->Value(); + } + } + return 0; +} + + +TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + ClearError(); +} + +TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} + + +#ifdef TIXML_USE_STL +TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} +#endif + + +TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + copy.CopyTo( this ); +} + + +TiXmlDocument& TiXmlDocument::operator=( const TiXmlDocument& copy ) +{ + Clear(); + copy.CopyTo( this ); + return *this; +} + + +bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) +{ + return LoadFile( Value(), encoding ); +} + + +bool TiXmlDocument::SaveFile() const +{ + return SaveFile( Value() ); +} + +bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) +{ + TIXML_STRING filename( _filename ); + value = filename; + + // reading in binary mode so that tinyxml can normalize the EOL + FILE* file = TiXmlFOpen( value.c_str (), "rb" ); + + if ( file ) + { + bool result = LoadFile( file, encoding ); + fclose( file ); + return result; + } + else + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } +} + +bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) +{ + if ( !file ) + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Delete the existing data: + Clear(); + location.Clear(); + + // Get the file size, so we can pre-allocate the string. HUGE speed impact. + long length = 0; + fseek( file, 0, SEEK_END ); + length = ftell( file ); + fseek( file, 0, SEEK_SET ); + + // Strange case, but good to handle up front. + if ( length <= 0 ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Subtle bug here. TinyXml did use fgets. But from the XML spec: + // 2.11 End-of-Line Handling + // + // + // ...the XML processor MUST behave as if it normalized all line breaks in external + // parsed entities (including the document entity) on input, before parsing, by translating + // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to + // a single #xA character. + // + // + // It is not clear fgets does that, and certainly isn't clear it works cross platform. + // Generally, you expect fgets to translate from the convention of the OS to the c/unix + // convention, and not work generally. + + /* + while( fgets( buf, sizeof(buf), file ) ) + { + data += buf; + } + */ + + char* buf = new char[ length+1 ]; + buf[0] = 0; + + if ( fread( buf, length, 1, file ) != 1 ) { + delete [] buf; + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Process the buffer in place to normalize new lines. (See comment above.) + // Copies from the 'p' to 'q' pointer, where p can advance faster if + // a newline-carriage return is hit. + // + // Wikipedia: + // Systems based on ASCII or a compatible character set use either LF (Line feed, '\n', 0x0A, 10 in decimal) or + // CR (Carriage return, '\r', 0x0D, 13 in decimal) individually, or CR followed by LF (CR+LF, 0x0D 0x0A)... + // * LF: Multics, Unix and Unix-like systems (GNU/Linux, AIX, Xenix, Mac OS X, FreeBSD, etc.), BeOS, Amiga, RISC OS, and others + // * CR+LF: DEC RT-11 and most other early non-Unix, non-IBM OSes, CP/M, MP/M, DOS, OS/2, Microsoft Windows, Symbian OS + // * CR: Commodore 8-bit machines, Apple II family, Mac OS up to version 9 and OS-9 + + const char* p = buf; // the read head + char* q = buf; // the write head + const char CR = 0x0d; + const char LF = 0x0a; + + buf[length] = 0; + while( *p ) { + assert( p < (buf+length) ); + assert( q <= (buf+length) ); + assert( q <= p ); + + if ( *p == CR ) { + *q++ = LF; + p++; + if ( *p == LF ) { // check for CR+LF (and skip LF) + p++; + } + } + else { + *q++ = *p++; + } + } + assert( q <= (buf+length) ); + *q = 0; + + Parse( buf, 0, encoding ); + + delete [] buf; + return !Error(); +} + + +bool TiXmlDocument::SaveFile( const char * filename ) const +{ + // The old c stuff lives on... + FILE* fp = TiXmlFOpen( filename, "w" ); + if ( fp ) + { + bool result = SaveFile( fp ); + fclose( fp ); + return result; + } + return false; +} + + +bool TiXmlDocument::SaveFile( FILE* fp ) const +{ + if ( useMicrosoftBOM ) + { + const unsigned char TIXML_UTF_LEAD_0 = 0xefU; + const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; + const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + + fputc( TIXML_UTF_LEAD_0, fp ); + fputc( TIXML_UTF_LEAD_1, fp ); + fputc( TIXML_UTF_LEAD_2, fp ); + } + Print( fp, 0 ); + return (ferror(fp) == 0); +} + + +void TiXmlDocument::CopyTo( TiXmlDocument* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->error = error; + target->errorId = errorId; + target->errorDesc = errorDesc; + target->tabsize = tabsize; + target->errorLocation = errorLocation; + target->useMicrosoftBOM = useMicrosoftBOM; + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + + +TiXmlNode* TiXmlDocument::Clone() const +{ + TiXmlDocument* clone = new TiXmlDocument(); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlDocument::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + node->Print( cfile, depth ); + fprintf( cfile, "\n" ); + } +} + + +bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +const TiXmlAttribute* TiXmlAttribute::Next() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} + +/* +TiXmlAttribute* TiXmlAttribute::Next() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} +*/ + +const TiXmlAttribute* TiXmlAttribute::Previous() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} + +/* +TiXmlAttribute* TiXmlAttribute::Previous() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} +*/ + +void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + TIXML_STRING n, v; + + EncodeString( name, &n ); + EncodeString( value, &v ); + + if (value.find ('\"') == TIXML_STRING::npos) { + if ( cfile ) { + fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\""; + } + } + else { + if ( cfile ) { + fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'"; + } + } +} + + +int TiXmlAttribute::QueryIntValue( int* ival ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +int TiXmlAttribute::QueryDoubleValue( double* dval ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +void TiXmlAttribute::SetIntValue( int _value ) +{ + char buf [64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value); + #else + sprintf (buf, "%d", _value); + #endif + SetValue (buf); +} + +void TiXmlAttribute::SetDoubleValue( double _value ) +{ + char buf [256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%g", _value); + #else + sprintf (buf, "%g", _value); + #endif + SetValue (buf); +} + +int TiXmlAttribute::IntValue() const +{ + return atoi (value.c_str ()); +} + +double TiXmlAttribute::DoubleValue() const +{ + return atof (value.c_str ()); +} + + +TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) +{ + copy.CopyTo( this ); +} + + +TiXmlComment& TiXmlComment::operator=( const TiXmlComment& base ) +{ + Clear(); + base.CopyTo( this ); + return *this; +} + + +void TiXmlComment::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlComment::CopyTo( TiXmlComment* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlComment::Clone() const +{ + TiXmlComment* clone = new TiXmlComment(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlText::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + if ( cdata ) + { + int i; + fprintf( cfile, "\n" ); + for ( i=0; i\n", value.c_str() ); // unformatted output + } + else + { + TIXML_STRING buffer; + EncodeString( value, &buffer ); + fprintf( cfile, "%s", buffer.c_str() ); + } +} + + +void TiXmlText::CopyTo( TiXmlText* target ) const +{ + TiXmlNode::CopyTo( target ); + target->cdata = cdata; +} + + +bool TiXmlText::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlText::Clone() const +{ + TiXmlText* clone = 0; + clone = new TiXmlText( "" ); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlDeclaration::TiXmlDeclaration( const char * _version, + const char * _encoding, + const char * _standalone ) + : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} + + +#ifdef TIXML_USE_STL +TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ) + : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} +#endif + + +TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) + : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) +{ + copy.CopyTo( this ); +} + + +TiXmlDeclaration& TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) +{ + Clear(); + copy.CopyTo( this ); + return *this; +} + + +void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + if ( cfile ) fprintf( cfile, "" ); + if ( str ) (*str) += "?>"; +} + + +void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->version = version; + target->encoding = encoding; + target->standalone = standalone; +} + + +bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlDeclaration::Clone() const +{ + TiXmlDeclaration* clone = new TiXmlDeclaration(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlUnknown::Print( FILE* cfile, int depth ) const +{ + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlUnknown::Clone() const +{ + TiXmlUnknown* clone = new TiXmlUnknown(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlAttributeSet::TiXmlAttributeSet() +{ + sentinel.next = &sentinel; + sentinel.prev = &sentinel; +} + + +TiXmlAttributeSet::~TiXmlAttributeSet() +{ + assert( sentinel.next == &sentinel ); + assert( sentinel.prev == &sentinel ); +} + + +void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) +{ + #ifdef TIXML_USE_STL + assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set. + #else + assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. + #endif + + addMe->next = &sentinel; + addMe->prev = sentinel.prev; + + sentinel.prev->next = addMe; + sentinel.prev = addMe; +} + +void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) +{ + TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node == removeMe ) + { + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = 0; + node->prev = 0; + return; + } + } + assert( 0 ); // we tried to remove a non-linked attribute. +} + + +#ifdef TIXML_USE_STL +TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} + +TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const std::string& _name ) +{ + TiXmlAttribute* attrib = Find( _name ); + if ( !attrib ) { + attrib = new TiXmlAttribute(); + Add( attrib ); + attrib->SetName( _name ); + } + return attrib; +} +#endif + + +TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} + + +TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const char* _name ) +{ + TiXmlAttribute* attrib = Find( _name ); + if ( !attrib ) { + attrib = new TiXmlAttribute(); + Add( attrib ); + attrib->SetName( _name ); + } + return attrib; +} + + +#ifdef TIXML_USE_STL +std::istream& operator>> (std::istream & in, TiXmlNode & base) +{ + TIXML_STRING tag; + tag.reserve( 8 * 1000 ); + base.StreamIn( &in, &tag ); + + base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); + return in; +} +#endif + + +#ifdef TIXML_USE_STL +std::ostream& operator<< (std::ostream & out, const TiXmlNode & base) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out << printer.Str(); + + return out; +} + + +std::string& operator<< (std::string& out, const TiXmlNode& base ) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out.append( printer.Str() ); + + return out; +} +#endif + + +TiXmlHandle TiXmlHandle::FirstChild() const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement() const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild(); + for ( i=0; + child && iNextSibling(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild( value ); + for ( i=0; + child && iNextSibling( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement(); + for ( i=0; + child && iNextSiblingElement(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement( value ); + for ( i=0; + child && iNextSiblingElement( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +bool TiXmlPrinter::VisitEnter( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitExit( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + DoIndent(); + buffer += "<"; + buffer += element.Value(); + + for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() ) + { + buffer += " "; + attrib->Print( 0, 0, &buffer ); + } + + if ( !element.FirstChild() ) + { + buffer += " />"; + DoLineBreak(); + } + else + { + buffer += ">"; + if ( element.FirstChild()->ToText() + && element.LastChild() == element.FirstChild() + && element.FirstChild()->ToText()->CDATA() == false ) + { + simpleTextPrint = true; + // no DoLineBreak()! + } + else + { + DoLineBreak(); + } + } + ++depth; + return true; +} + + +bool TiXmlPrinter::VisitExit( const TiXmlElement& element ) +{ + --depth; + if ( !element.FirstChild() ) + { + // nothing. + } + else + { + if ( simpleTextPrint ) + { + simpleTextPrint = false; + } + else + { + DoIndent(); + } + buffer += ""; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlText& text ) +{ + if ( text.CDATA() ) + { + DoIndent(); + buffer += ""; + DoLineBreak(); + } + else if ( simpleTextPrint ) + { + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + } + else + { + DoIndent(); + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration ) +{ + DoIndent(); + declaration.Print( 0, 0, &buffer ); + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlComment& comment ) +{ + DoIndent(); + buffer += ""; + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown ) +{ + DoIndent(); + buffer += "<"; + buffer += unknown.Value(); + buffer += ">"; + DoLineBreak(); + return true; +} + diff --git a/opm/core/utility/parameters/tinyxml/tinyxml.h b/opm/core/utility/parameters/tinyxml/tinyxml.h new file mode 100644 index 00000000..c13ba392 --- /dev/null +++ b/opm/core/utility/parameters/tinyxml/tinyxml.h @@ -0,0 +1,1807 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TINYXML_INCLUDED +#define TINYXML_INCLUDED + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4530 ) +#pragma warning( disable : 4786 ) +#endif + +#include +#include +#include +#include +#include + +// Help out windows: +#if defined( _DEBUG ) && !defined( DEBUG ) +#define DEBUG +#endif + +#define TIXML_USE_STL + +#ifdef TIXML_USE_STL + #include + #include + #include + #define TIXML_STRING std::string +#else + #include "tinystr.h" + #define TIXML_STRING TiXmlString +#endif + +// Deprecated library function hell. Compilers want to use the +// new safe versions. This probably doesn't fully address the problem, +// but it gets closer. There are too many compilers for me to fully +// test. If you get compilation troubles, undefine TIXML_SAFE +#define TIXML_SAFE + +#ifdef TIXML_SAFE + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + // Microsoft visual studio, version 2005 and higher. + #define TIXML_SNPRINTF _snprintf_s + #define TIXML_SSCANF sscanf_s + #elif defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + //#pragma message( "Using _sn* functions." ) + #define TIXML_SNPRINTF _snprintf + #define TIXML_SSCANF sscanf + #elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + //#warning( "Using sn* functions." ) + #define TIXML_SNPRINTF snprintf + #define TIXML_SSCANF sscanf + #else + #define TIXML_SNPRINTF snprintf + #define TIXML_SSCANF sscanf + #endif +#endif + +class TiXmlDocument; +class TiXmlElement; +class TiXmlComment; +class TiXmlUnknown; +class TiXmlAttribute; +class TiXmlText; +class TiXmlDeclaration; +class TiXmlParsingData; + +const int TIXML_MAJOR_VERSION = 2; +const int TIXML_MINOR_VERSION = 6; +const int TIXML_PATCH_VERSION = 2; + +/* Internal structure for tracking location of items + in the XML file. +*/ +struct TiXmlCursor +{ + TiXmlCursor() { Clear(); } + void Clear() { row = col = -1; } + + int row; // 0 based. + int col; // 0 based. +}; + + +/** + Implements the interface to the "Visitor pattern" (see the Accept() method.) + If you call the Accept() method, it requires being passed a TiXmlVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves + are simply called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, no children of this node or its sibilings will be Visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting. + + You should never change the document from a callback. + + @sa TiXmlNode::Accept() +*/ +class TiXmlVisitor +{ +public: + virtual ~TiXmlVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { return true; } + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { return true; } + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ ) { return true; } + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& /*element*/ ) { return true; } + + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { return true; } + /// Visit a text node + virtual bool Visit( const TiXmlText& /*text*/ ) { return true; } + /// Visit a comment node + virtual bool Visit( const TiXmlComment& /*comment*/ ) { return true; } + /// Visit an unknown node + virtual bool Visit( const TiXmlUnknown& /*unknown*/ ) { return true; } +}; + +// Only used by Attribute::Query functions +enum +{ + TIXML_SUCCESS, + TIXML_NO_ATTRIBUTE, + TIXML_WRONG_TYPE +}; + + +// Used by the parsing routines. +enum TiXmlEncoding +{ + TIXML_ENCODING_UNKNOWN, + TIXML_ENCODING_UTF8, + TIXML_ENCODING_LEGACY +}; + +const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; + +/** TiXmlBase is a base class for every class in TinyXml. + It does little except to establish that TinyXml classes + can be printed and provide some utility functions. + + In XML, the document and elements can contain + other elements and other types of nodes. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + A Decleration contains: Attributes (not on tree) + @endverbatim +*/ +class TiXmlBase +{ + friend class TiXmlNode; + friend class TiXmlElement; + friend class TiXmlDocument; + +public: + TiXmlBase() : userData(0) {} + virtual ~TiXmlBase() {} + + /** All TinyXml classes can print themselves to a filestream + or the string class (TiXmlString in non-STL mode, std::string + in STL mode.) Either or both cfile and str can be null. + + This is a formatted print, and will insert + tabs and newlines. + + (For an unformatted stream, use the << operator.) + */ + virtual void Print( FILE* cfile, int depth ) const = 0; + + /** The world does not agree on whether white space should be kept or + not. In order to make everyone happy, these global, static functions + are provided to set whether or not TinyXml will condense all white space + into a single space or not. The default is to condense. Note changing this + value is not thread safe. + */ + static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; } + + /// Return the current white space setting. + static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; } + + /** Return the position, in the original source file, of this node or attribute. + The row and column are 1-based. (That is the first row and first column is + 1,1). If the returns values are 0 or less, then the parser does not have + a row and column value. + + Generally, the row and column value will be set when the TiXmlDocument::Load(), + TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set + when the DOM was created from operator>>. + + The values reflect the initial load. Once the DOM is modified programmatically + (by adding or changing nodes and attributes) the new values will NOT update to + reflect changes in the document. + + There is a minor performance cost to computing the row and column. Computation + can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. + + @sa TiXmlDocument::SetTabSize() + */ + int Row() const { return location.row + 1; } + int Column() const { return location.col + 1; } ///< See Row() + + void SetUserData( void* user ) { userData = user; } ///< Set a pointer to arbitrary user data. + void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data. + const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data. + + // Table that returs, for a given lead byte, the total number of bytes + // in the UTF-8 sequence. + static const int utf8ByteTable[256]; + + virtual const char* Parse( const char* p, + TiXmlParsingData* data, + TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0; + + /** Expands entities in a string. Note this should not contian the tag's '<', '>', etc, + or they will be transformed into entities! + */ + static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out ); + + enum + { + TIXML_NO_ERROR = 0, + TIXML_ERROR, + TIXML_ERROR_OPENING_FILE, + TIXML_ERROR_PARSING_ELEMENT, + TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, + TIXML_ERROR_READING_ELEMENT_VALUE, + TIXML_ERROR_READING_ATTRIBUTES, + TIXML_ERROR_PARSING_EMPTY, + TIXML_ERROR_READING_END_TAG, + TIXML_ERROR_PARSING_UNKNOWN, + TIXML_ERROR_PARSING_COMMENT, + TIXML_ERROR_PARSING_DECLARATION, + TIXML_ERROR_DOCUMENT_EMPTY, + TIXML_ERROR_EMBEDDED_NULL, + TIXML_ERROR_PARSING_CDATA, + TIXML_ERROR_DOCUMENT_TOP_ONLY, + + TIXML_ERROR_STRING_COUNT + }; + +protected: + + static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); + + inline static bool IsWhiteSpace( char c ) + { + return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); + } + inline static bool IsWhiteSpace( int c ) + { + if ( c < 256 ) + return IsWhiteSpace( (char) c ); + return false; // Again, only truly correct for English/Latin...but usually works. + } + + #ifdef TIXML_USE_STL + static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ); + static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag ); + #endif + + /* Reads an XML name into the string provided. Returns + a pointer just past the last character of the name, + or 0 if the function has an error. + */ + static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding ); + + /* Reads text. Returns a pointer past the given end tag. + Wickedly complex options, but it keeps the (sensitive) code in one place. + */ + static const char* ReadText( const char* in, // where to start + TIXML_STRING* text, // the string read + bool ignoreWhiteSpace, // whether to keep the white space + const char* endTag, // what ends this text + bool ignoreCase, // whether to ignore case in the end tag + TiXmlEncoding encoding ); // the current encoding + + // If an entity has been found, transform it into a character. + static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding ); + + // Get a character, while interpreting entities. + // The length can be from 0 to 4 bytes. + inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding ) + { + assert( p ); + if ( encoding == TIXML_ENCODING_UTF8 ) + { + *length = utf8ByteTable[ *((const unsigned char*)p) ]; + assert( *length >= 0 && *length < 5 ); + } + else + { + *length = 1; + } + + if ( *length == 1 ) + { + if ( *p == '&' ) + return GetEntity( p, _value, length, encoding ); + *_value = *p; + return p+1; + } + else if ( *length ) + { + //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe), + // and the null terminator isn't needed + for( int i=0; p[i] && i<*length; ++i ) { + _value[i] = p[i]; + } + return p + (*length); + } + else + { + // Not valid text. + return 0; + } + } + + // Return true if the next characters in the stream are any of the endTag sequences. + // Ignore case only works for english, and should only be relied on when comparing + // to English words: StringEqual( p, "version", true ) is fine. + static bool StringEqual( const char* p, + const char* endTag, + bool ignoreCase, + TiXmlEncoding encoding ); + + static const char* errorString[ TIXML_ERROR_STRING_COUNT ]; + + TiXmlCursor location; + + /// Field containing a generic user pointer + void* userData; + + // None of these methods are reliable for any language except English. + // Good for approximation, not great for accuracy. + static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ); + static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ); + inline static int ToLower( int v, TiXmlEncoding encoding ) + { + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( v < 128 ) return tolower( v ); + return v; + } + else + { + return tolower( v ); + } + } + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + +private: + TiXmlBase( const TiXmlBase& ); // not implemented. + void operator=( const TiXmlBase& base ); // not allowed. + + struct Entity + { + const char* str; + unsigned int strLength; + char chr; + }; + enum + { + NUM_ENTITY = 5, + MAX_ENTITY_LENGTH = 6 + + }; + static Entity entity[ NUM_ENTITY ]; + static bool condenseWhiteSpace; +}; + + +/** The parent class for everything in the Document Object Model. + (Except for attributes). + Nodes have siblings, a parent, and children. A node can be + in a document, or stand on its own. The type of a TiXmlNode + can be queried, and it can be cast to its more defined type. +*/ +class TiXmlNode : public TiXmlBase +{ + friend class TiXmlDocument; + friend class TiXmlElement; + +public: + #ifdef TIXML_USE_STL + + /** An input stream operator, for every class. Tolerant of newlines and + formatting, but doesn't expect them. + */ + friend std::istream& operator >> (std::istream& in, TiXmlNode& base); + + /** An output stream operator, for every class. Note that this outputs + without any newlines or formatting, as opposed to Print(), which + includes tabs and new lines. + + The operator<< and operator>> are not completely symmetric. Writing + a node to a stream is very well defined. You'll get a nice stream + of output, without any extra whitespace or newlines. + + But reading is not as well defined. (As it always is.) If you create + a TiXmlElement (for example) and read that from an input stream, + the text needs to define an element or junk will result. This is + true of all input streams, but it's worth keeping in mind. + + A TiXmlDocument will read nodes until it reads a root element, and + all the children of that root element. + */ + friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base); + + /// Appends the XML node or attribute to a std::string. + friend std::string& operator<< (std::string& out, const TiXmlNode& base ); + + #endif + + /** The types of XML nodes supported by TinyXml. (All the + unsupported types are picked up by UNKNOWN.) + */ + enum NodeType + { + TINYXML_DOCUMENT, + TINYXML_ELEMENT, + TINYXML_COMMENT, + TINYXML_UNKNOWN, + TINYXML_TEXT, + TINYXML_DECLARATION, + TINYXML_TYPECOUNT + }; + + virtual ~TiXmlNode(); + + /** The meaning of 'value' changes for the specific type of + TiXmlNode. + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + + The subclasses will wrap this function. + */ + const char *Value() const { return value.c_str (); } + + #ifdef TIXML_USE_STL + /** Return Value() as a std::string. If you only use STL, + this is more efficient than calling Value(). + Only available in STL mode. + */ + const std::string& ValueStr() const { return value; } + #endif + + const TIXML_STRING& ValueTStr() const { return value; } + + /** Changes the value of the node. Defined as: + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + void SetValue(const char * _value) { value = _value;} + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Delete all the children of this node. Does not affect 'this'. + void Clear(); + + /// One step up the DOM. + TiXmlNode* Parent() { return parent; } + const TiXmlNode* Parent() const { return parent; } + + const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children. + TiXmlNode* FirstChild() { return firstChild; } + const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found. + /// The first child of this node with the matching 'value'. Will be null if none found. + TiXmlNode* FirstChild( const char * _value ) { + // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe) + // call the method, cast the return back to non-const. + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value )); + } + const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children. + TiXmlNode* LastChild() { return lastChild; } + + const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children. + TiXmlNode* LastChild( const char * _value ) { + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value )); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form. + #endif + + /** An alternate way to walk the children of a node. + One way to iterate over nodes is: + @verbatim + for( child = parent->FirstChild(); child; child = child->NextSibling() ) + @endverbatim + + IterateChildren does the same thing with the syntax: + @verbatim + child = 0; + while( child = parent->IterateChildren( child ) ) + @endverbatim + + IterateChildren takes the previous child as input and finds + the next one. If the previous child is null, it returns the + first. IterateChildren will return null when done. + */ + const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) ); + } + + /// This flavor of IterateChildren searches for children with a particular 'value' + const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + #endif + + /** Add a new node related to this. Adds a child past the LastChild. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertEndChild( const TiXmlNode& addThis ); + + + /** Add a new node related to this. Adds a child past the LastChild. + + NOTE: the node to be added is passed by pointer, and will be + henceforth owned (and deleted) by tinyXml. This method is efficient + and avoids an extra copy, but should be used with care as it + uses a different memory model than the other insert functions. + + @sa InsertEndChild + */ + TiXmlNode* LinkEndChild( TiXmlNode* addThis ); + + /** Add a new node related to this. Adds a child before the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ); + + /** Add a new node related to this. Adds a child after the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ); + + /** Replace a child of this node. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ); + + /// Delete a child of this node. + bool RemoveChild( TiXmlNode* removeThis ); + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling() const { return prev; } + TiXmlNode* PreviousSibling() { return prev; } + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling( const char * ) const; + TiXmlNode* PreviousSibling( const char *_prev ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Navigate to a sibling node. + const TiXmlNode* NextSibling() const { return next; } + TiXmlNode* NextSibling() { return next; } + + /// Navigate to a sibling node with the given 'value'. + const TiXmlNode* NextSibling( const char * ) const; + TiXmlNode* NextSibling( const char* _next ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement() const; + TiXmlElement* NextSiblingElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement( const char * ) const; + TiXmlElement* NextSiblingElement( const char *_next ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement() const; + TiXmlElement* FirstChildElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() ); + } + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement( const char * _value ) const; + TiXmlElement* FirstChildElement( const char * _value ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /** Query the type (as an enumerated value, above) of this node. + The possible types are: TINYXML_DOCUMENT, TINYXML_ELEMENT, TINYXML_COMMENT, + TINYXML_UNKNOWN, TINYXML_TEXT, and TINYXML_DECLARATION. + */ + int Type() const { return type; } + + /** Return a pointer to the Document this node lives in. + Returns null if not in a document. + */ + const TiXmlDocument* GetDocument() const; + TiXmlDocument* GetDocument() { + return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() ); + } + + /// Returns true if this node has no children. + bool NoChildren() const { return !firstChild; } + + virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + /** Create an exact duplicate of this node and return it. The memory must be deleted + by the caller. + */ + virtual TiXmlNode* Clone() const = 0; + + /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the TiXmlVisitor interface. + + This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + TiXmlPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( TiXmlVisitor* visitor ) const = 0; + +protected: + TiXmlNode( NodeType _type ); + + // Copy to the allocated object. Shared functionality between Clone, Copy constructor, + // and the assignment operator. + void CopyTo( TiXmlNode* target ) const; + + #ifdef TIXML_USE_STL + // The real work of the input operator. + virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0; + #endif + + // Figure out what is at *p, and parse it. Returns null if it is not an xml node. + TiXmlNode* Identify( const char* start, TiXmlEncoding encoding ); + + TiXmlNode* parent; + NodeType type; + + TiXmlNode* firstChild; + TiXmlNode* lastChild; + + TIXML_STRING value; + + TiXmlNode* prev; + TiXmlNode* next; + +private: + TiXmlNode( const TiXmlNode& ); // not implemented. + void operator=( const TiXmlNode& base ); // not allowed. +}; + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not TiXmlNodes, since they are not + part of the tinyXML document object model. There are other + suggested ways to look at this problem. +*/ +class TiXmlAttribute : public TiXmlBase +{ + friend class TiXmlAttributeSet; + +public: + /// Construct an empty attribute. + TiXmlAttribute() : TiXmlBase() + { + document = 0; + prev = next = 0; + } + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlAttribute( const std::string& _name, const std::string& _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + #endif + + /// Construct an attribute with a name and value. + TiXmlAttribute( const char * _name, const char * _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + + const char* Name() const { return name.c_str(); } ///< Return the name of this attribute. + const char* Value() const { return value.c_str(); } ///< Return the value of this attribute. + #ifdef TIXML_USE_STL + const std::string& ValueStr() const { return value; } ///< Return the value of this attribute. + #endif + int IntValue() const; ///< Return the value of this attribute, converted to an integer. + double DoubleValue() const; ///< Return the value of this attribute, converted to a double. + + // Get the tinyxml string representation + const TIXML_STRING& NameTStr() const { return name; } + + /** QueryIntValue examines the value string. It is an alternative to the + IntValue() method with richer error checking. + If the value is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. + + A specialized but useful call. Note that for success it returns 0, + which is the opposite of almost all other TinyXml calls. + */ + int QueryIntValue( int* _value ) const; + /// QueryDoubleValue examines the value string. See QueryIntValue(). + int QueryDoubleValue( double* _value ) const; + + void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute. + void SetValue( const char* _value ) { value = _value; } ///< Set the value. + + void SetIntValue( int _value ); ///< Set the value from an integer. + void SetDoubleValue( double _value ); ///< Set the value from a double. + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetName( const std::string& _name ) { name = _name; } + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Get the next sibling attribute in the DOM. Returns null at end. + const TiXmlAttribute* Next() const; + TiXmlAttribute* Next() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() ); + } + + /// Get the previous sibling attribute in the DOM. Returns null at beginning. + const TiXmlAttribute* Previous() const; + TiXmlAttribute* Previous() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() ); + } + + bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; } + bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; } + bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; } + + /* Attribute parsing starts: first letter of the name + returns: the next char after the value end quote + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + // Prints this Attribute to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + + // [internal use] + // Set the document pointer so the attribute can report errors. + void SetDocument( TiXmlDocument* doc ) { document = doc; } + +private: + TiXmlAttribute( const TiXmlAttribute& ); // not implemented. + void operator=( const TiXmlAttribute& base ); // not allowed. + + TiXmlDocument* document; // A pointer back to a document, for error reporting. + TIXML_STRING name; + TIXML_STRING value; + TiXmlAttribute* prev; + TiXmlAttribute* next; +}; + + +/* A class used to manage a group of attributes. + It is only used internally, both by the ELEMENT and the DECLARATION. + + The set can be changed transparent to the Element and Declaration + classes that use it, but NOT transparent to the Attribute + which has to implement a next() and previous() method. Which makes + it a bit problematic and prevents the use of STL. + + This version is implemented with circular lists because: + - I like circular lists + - it demonstrates some independence from the (typical) doubly linked list. +*/ +class TiXmlAttributeSet +{ +public: + TiXmlAttributeSet(); + ~TiXmlAttributeSet(); + + void Add( TiXmlAttribute* attribute ); + void Remove( TiXmlAttribute* attribute ); + + const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + + TiXmlAttribute* Find( const char* _name ) const; + TiXmlAttribute* FindOrCreate( const char* _name ); + +# ifdef TIXML_USE_STL + TiXmlAttribute* Find( const std::string& _name ) const; + TiXmlAttribute* FindOrCreate( const std::string& _name ); +# endif + + +private: + //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element), + //*ME: this class must be also use a hidden/disabled copy-constructor !!! + TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed + void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute) + + TiXmlAttribute sentinel; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TiXmlElement : public TiXmlNode +{ +public: + /// Construct an element. + TiXmlElement (const char * in_value); + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlElement( const std::string& _value ); + #endif + + TiXmlElement( const TiXmlElement& ); + + TiXmlElement& operator=( const TiXmlElement& base ); + + virtual ~TiXmlElement(); + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + */ + const char* Attribute( const char* name ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an integer, + the integer value will be put in the return 'i', if 'i' + is non-null. + */ + const char* Attribute( const char* name, int* i ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an double, + the double value will be put in the return 'd', if 'd' + is non-null. + */ + const char* Attribute( const char* name, double* d ) const; + + /** QueryIntAttribute examines the attribute - it is an alternative to the + Attribute() method with richer error checking. + If the attribute is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. If the attribute + does not exist, then TIXML_NO_ATTRIBUTE is returned. + */ + int QueryIntAttribute( const char* name, int* _value ) const; + /// QueryUnsignedAttribute examines the attribute - see QueryIntAttribute(). + int QueryUnsignedAttribute( const char* name, unsigned* _value ) const; + /** QueryBoolAttribute examines the attribute - see QueryIntAttribute(). + Note that '1', 'true', or 'yes' are considered true, while '0', 'false' + and 'no' are considered false. + */ + int QueryBoolAttribute( const char* name, bool* _value ) const; + /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). + int QueryDoubleAttribute( const char* name, double* _value ) const; + /// QueryFloatAttribute examines the attribute - see QueryIntAttribute(). + int QueryFloatAttribute( const char* name, float* _value ) const { + double d; + int result = QueryDoubleAttribute( name, &d ); + if ( result == TIXML_SUCCESS ) { + *_value = (float)d; + } + return result; + } + + #ifdef TIXML_USE_STL + /// QueryStringAttribute examines the attribute - see QueryIntAttribute(). + int QueryStringAttribute( const char* name, std::string* _value ) const { + const char* cstr = Attribute( name ); + if ( cstr ) { + *_value = std::string( cstr ); + return TIXML_SUCCESS; + } + return TIXML_NO_ATTRIBUTE; + } + + /** Template form of the attribute query which will try to read the + attribute into the specified type. Very easy, very powerful, but + be careful to make sure to call this with the correct type. + + NOTE: This method doesn't work correctly for 'string' types that contain spaces. + + @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE + */ + template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + std::stringstream sstream( node->ValueStr() ); + sstream >> *outValue; + if ( !sstream.fail() ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; + } + + int QueryValueAttribute( const std::string& name, std::string* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + *outValue = node->ValueStr(); + return TIXML_SUCCESS; + } + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char* name, const char * _value ); + + #ifdef TIXML_USE_STL + const std::string* Attribute( const std::string& name ) const; + const std::string* Attribute( const std::string& name, int* i ) const; + const std::string* Attribute( const std::string& name, double* d ) const; + int QueryIntAttribute( const std::string& name, int* _value ) const; + int QueryDoubleAttribute( const std::string& name, double* _value ) const; + + /// STL std::string form. + void SetAttribute( const std::string& name, const std::string& _value ); + ///< STL std::string form. + void SetAttribute( const std::string& name, int _value ); + ///< STL std::string form. + void SetDoubleAttribute( const std::string& name, double value ); + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char * name, int value ); + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetDoubleAttribute( const char * name, double value ); + + /** Deletes an attribute with the given name. + */ + void RemoveAttribute( const char * name ); + #ifdef TIXML_USE_STL + void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form. + #endif + + const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element. + TiXmlAttribute* FirstAttribute() { return attributeSet.First(); } + const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element. + TiXmlAttribute* LastAttribute() { return attributeSet.Last(); } + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the TiXmlText child + and accessing it directly. + + If the first child of 'this' is a TiXmlText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + This is text + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + This is text + @endverbatim + GetText() will return "This is ". + + WARNING: GetText() accesses a child node - don't become confused with the + similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are + safe type casts on the referenced node. + */ + const char* GetText() const; + + /// Creates a new Element and returns it - the returned element is a copy. + virtual TiXmlNode* Clone() const; + // Print the Element to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: next char past '<' + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + + void CopyTo( TiXmlElement* target ) const; + void ClearThis(); // like clear, but initializes 'this' object as well + + // Used to be public [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + /* [internal use] + Reads the "value" of the element -- another element, or text. + This should terminate with the current end tag. + */ + const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + +private: + TiXmlAttributeSet attributeSet; +}; + + +/** An XML comment. +*/ +class TiXmlComment : public TiXmlNode +{ +public: + /// Constructs an empty comment. + TiXmlComment() : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) {} + /// Construct a comment from text. + TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) { + SetValue( _value ); + } + TiXmlComment( const TiXmlComment& ); + TiXmlComment& operator=( const TiXmlComment& base ); + + virtual ~TiXmlComment() {} + + /// Returns a copy of this Comment. + virtual TiXmlNode* Clone() const; + // Write this Comment to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: at the ! of the !-- + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlComment* target ) const; + + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif +// virtual void StreamOut( TIXML_OSTREAM * out ) const; + +private: + +}; + + +/** XML text. A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCDATA() and query it with CDATA(). +*/ +class TiXmlText : public TiXmlNode +{ + friend class TiXmlElement; +public: + /** Constructor for text element. By default, it is treated as + normal, encoded text. If you want it be output as a CDATA text + element, set the parameter _cdata to 'true' + */ + TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT) + { + SetValue( initValue ); + cdata = false; + } + virtual ~TiXmlText() {} + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT) + { + SetValue( initValue ); + cdata = false; + } + #endif + + TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TINYXML_TEXT ) { copy.CopyTo( this ); } + TiXmlText& operator=( const TiXmlText& base ) { base.CopyTo( this ); return *this; } + + // Write this text object to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /// Queries whether this represents text using a CDATA section. + bool CDATA() const { return cdata; } + /// Turns on or off a CDATA representation of text. + void SetCDATA( bool _cdata ) { cdata = _cdata; } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + /// [internal use] Creates a new Element and returns it. + virtual TiXmlNode* Clone() const; + void CopyTo( TiXmlText* target ) const; + + bool Blank() const; // returns true if all white space and new lines + // [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + bool cdata; // true if this should be input and output as a CDATA style text element +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + + @endverbatim + + TinyXml will happily read or write files without a declaration, + however. There are 3 possible attributes to the declaration: + version, encoding, and standalone. + + Note: In this version of the code, the attributes are + handled as special cases, not generic attributes, simply + because there can only be at most 3 and they are always the same. +*/ +class TiXmlDeclaration : public TiXmlNode +{ +public: + /// Construct an empty declaration. + TiXmlDeclaration() : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) {} + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ); +#endif + + /// Construct. + TiXmlDeclaration( const char* _version, + const char* _encoding, + const char* _standalone ); + + TiXmlDeclaration( const TiXmlDeclaration& copy ); + TiXmlDeclaration& operator=( const TiXmlDeclaration& copy ); + + virtual ~TiXmlDeclaration() {} + + /// Version. Will return an empty string if none was found. + const char *Version() const { return version.c_str (); } + /// Encoding. Will return an empty string if none was found. + const char *Encoding() const { return encoding.c_str (); } + /// Is this a standalone document? + const char *Standalone() const { return standalone.c_str (); } + + /// Creates a copy of this Declaration and returns it. + virtual TiXmlNode* Clone() const; + // Print this declaration to a FILE stream. + virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlDeclaration* target ) const; + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + + TIXML_STRING version; + TIXML_STRING encoding; + TIXML_STRING standalone; +}; + + +/** Any tag that tinyXml doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into TiXmlUnknowns. +*/ +class TiXmlUnknown : public TiXmlNode +{ +public: + TiXmlUnknown() : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) {} + virtual ~TiXmlUnknown() {} + + TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) { copy.CopyTo( this ); } + TiXmlUnknown& operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); return *this; } + + /// Creates a copy of this Unknown and returns it. + virtual TiXmlNode* Clone() const; + // Print this Unknown to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected: + void CopyTo( TiXmlUnknown* target ) const; + + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + +}; + + +/** Always the top level node. A document binds together all the + XML pieces. It can be saved, loaded, and printed to the screen. + The 'value' of a document node is the xml file name. +*/ +class TiXmlDocument : public TiXmlNode +{ +public: + /// Create an empty document, that has no name. + TiXmlDocument(); + /// Create a document with a name. The name of the document is also the filename of the xml. + TiXmlDocument( const char * documentName ); + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlDocument( const std::string& documentName ); + #endif + + TiXmlDocument( const TiXmlDocument& copy ); + TiXmlDocument& operator=( const TiXmlDocument& copy ); + + virtual ~TiXmlDocument() {} + + /** Load a file using the current document value. + Returns true if successful. Will delete any existing + document data before loading. + */ + bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the current document value. Returns true if successful. + bool SaveFile() const; + /// Load a file using the given filename. Returns true if successful. + bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given filename. Returns true if successful. + bool SaveFile( const char * filename ) const; + /** Load a file using the given FILE*. Returns true if successful. Note that this method + doesn't stream - the entire object pointed at by the FILE* + will be interpreted as an XML file. TinyXML doesn't stream in XML from the current + file location. Streaming may be added in the future. + */ + bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given FILE*. Returns true if successful. + bool SaveFile( FILE* ) const; + + #ifdef TIXML_USE_STL + bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. + { + return LoadFile( filename.c_str(), encoding ); + } + bool SaveFile( const std::string& filename ) const ///< STL std::string version. + { + return SaveFile( filename.c_str() ); + } + #endif + + /** Parse the given null terminated block of xml data. Passing in an encoding to this + method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml + to use that encoding, regardless of what TinyXml might otherwise try to detect. + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + + /** Get the root element -- the only top level element -- of the document. + In well formed XML, there should only be one. TinyXml is tolerant of + multiple elements at the document level. + */ + const TiXmlElement* RootElement() const { return FirstChildElement(); } + TiXmlElement* RootElement() { return FirstChildElement(); } + + /** If an error occurs, Error will be set to true. Also, + - The ErrorId() will contain the integer identifier of the error (not generally useful) + - The ErrorDesc() method will return the name of the error. (very useful) + - The ErrorRow() and ErrorCol() will return the location of the error (if known) + */ + bool Error() const { return error; } + + /// Contains a textual (english) description of the error if one occurs. + const char * ErrorDesc() const { return errorDesc.c_str (); } + + /** Generally, you probably want the error string ( ErrorDesc() ). But if you + prefer the ErrorId, this function will fetch it. + */ + int ErrorId() const { return errorId; } + + /** Returns the location (if known) of the error. The first column is column 1, + and the first row is row 1. A value of 0 means the row and column wasn't applicable + (memory errors, for example, have no row/column) or the parser lost the error. (An + error in the error reporting, in that case.) + + @sa SetTabSize, Row, Column + */ + int ErrorRow() const { return errorLocation.row+1; } + int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow() + + /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol()) + to report the correct values for row and column. It does not change the output + or input in any way. + + By calling this method, with a tab size + greater than 0, the row and column of each node and attribute is stored + when the file is loaded. Very useful for tracking the DOM back in to + the source file. + + The tab size is required for calculating the location of nodes. If not + set, the default of 4 is used. The tabsize is set per document. Setting + the tabsize to 0 disables row/column tracking. + + Note that row and column tracking is not supported when using operator>>. + + The tab size needs to be enabled before the parse or load. Correct usage: + @verbatim + TiXmlDocument doc; + doc.SetTabSize( 8 ); + doc.Load( "myfile.xml" ); + @endverbatim + + @sa Row, Column + */ + void SetTabSize( int _tabsize ) { tabsize = _tabsize; } + + int TabSize() const { return tabsize; } + + /** If you have handled the error, it can be reset with this call. The error + state is automatically cleared if you Parse a new XML block. + */ + void ClearError() { error = false; + errorId = 0; + errorDesc = ""; + errorLocation.row = errorLocation.col = 0; + //errorLocation.last = 0; + } + + /** Write the document to standard out using formatted printing ("pretty print"). */ + void Print() const { Print( stdout, 0 ); } + + /* Write the document to a string using formatted printing ("pretty print"). This + will allocate a character array (new char[]) and return it as a pointer. The + calling code pust call delete[] on the return char* to avoid a memory leak. + */ + //char* PrintToMemory() const; + + /// Print this Document to a FILE stream. + virtual void Print( FILE* cfile, int depth = 0 ) const; + // [internal use] + void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + + virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + // [internal use] + virtual TiXmlNode* Clone() const; + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + void CopyTo( TiXmlDocument* target ) const; + + bool error; + int errorId; + TIXML_STRING errorDesc; + int tabsize; + TiXmlCursor errorLocation; + bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write. +}; + + +/** + A TiXmlHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + + + + + + + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + TiXmlElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity + of such code. A TiXmlHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + TiXmlHandle docHandle( &document ); + TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + TiXmlHandle handleCopy = handle; + @endverbatim + + What they should not be used for is iteration: + + @verbatim + int i=0; + while ( true ) + { + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement(); + if ( !child ) + break; + // do something + ++i; + } + @endverbatim + + It seems reasonable, but it is in fact two embedded while loops. The Child method is + a linear walk to find the element, so this code would iterate much more than it needs + to. Instead, prefer: + + @verbatim + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement(); + + for( child; child; child=child->NextSiblingElement() ) + { + // do something + } + @endverbatim +*/ +class TiXmlHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + TiXmlHandle( TiXmlNode* _node ) { this->node = _node; } + /// Copy constructor + TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; } + TiXmlHandle operator=( const TiXmlHandle& ref ) { if ( &ref != this ) this->node = ref.node; return *this; } + + /// Return a handle to the first child node. + TiXmlHandle FirstChild() const; + /// Return a handle to the first child node with the given name. + TiXmlHandle FirstChild( const char * value ) const; + /// Return a handle to the first child element. + TiXmlHandle FirstChildElement() const; + /// Return a handle to the first child element with the given name. + TiXmlHandle FirstChildElement( const char * value ) const; + + /** Return a handle to the "index" child with the given name. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( const char* value, int index ) const; + /** Return a handle to the "index" child. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( int index ) const; + /** Return a handle to the "index" child element with the given name. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( const char* value, int index ) const; + /** Return a handle to the "index" child element. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( int index ) const; + + #ifdef TIXML_USE_STL + TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); } + TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); } + + TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); } + TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); } + #endif + + /** Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* ToNode() const { return node; } + /** Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); } + /** Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); } + /** Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); } + + /** @deprecated use ToNode. + Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* Node() const { return ToNode(); } + /** @deprecated use ToElement. + Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* Element() const { return ToElement(); } + /** @deprecated use ToText() + Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* Text() const { return ToText(); } + /** @deprecated use ToUnknown() + Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* Unknown() const { return ToUnknown(); } + +private: + TiXmlNode* node; +}; + + +/** Print to memory functionality. The TiXmlPrinter is useful when you need to: + + -# Print to memory (especially in non-STL mode) + -# Control formatting (line endings, etc.) + + When constructed, the TiXmlPrinter is in its default "pretty printing" mode. + Before calling Accept() you can call methods to control the printing + of the XML document. After TiXmlNode::Accept() is called, the printed document can + be accessed via the CStr(), Str(), and Size() methods. + + TiXmlPrinter uses the Visitor API. + @verbatim + TiXmlPrinter printer; + printer.SetIndent( "\t" ); + + doc.Accept( &printer ); + fprintf( stdout, "%s", printer.CStr() ); + @endverbatim +*/ +class TiXmlPrinter : public TiXmlVisitor +{ +public: + TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ), + buffer(), indent( " " ), lineBreak( "\n" ) {} + + virtual bool VisitEnter( const TiXmlDocument& doc ); + virtual bool VisitExit( const TiXmlDocument& doc ); + + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + virtual bool VisitExit( const TiXmlElement& element ); + + virtual bool Visit( const TiXmlDeclaration& declaration ); + virtual bool Visit( const TiXmlText& text ); + virtual bool Visit( const TiXmlComment& comment ); + virtual bool Visit( const TiXmlUnknown& unknown ); + + /** Set the indent characters for printing. By default 4 spaces + but tab (\t) is also useful, or null/empty string for no indentation. + */ + void SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; } + /// Query the indention string. + const char* Indent() { return indent.c_str(); } + /** Set the line breaking string. By default set to newline (\n). + Some operating systems prefer other characters, or can be + set to the null/empty string for no indenation. + */ + void SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; } + /// Query the current line breaking string. + const char* LineBreak() { return lineBreak.c_str(); } + + /** Switch over to "stream printing" which is the most dense formatting without + linebreaks. Common when the XML is needed for network transmission. + */ + void SetStreamPrinting() { indent = ""; + lineBreak = ""; + } + /// Return the result. + const char* CStr() { return buffer.c_str(); } + /// Return the length of the result string. + size_t Size() { return buffer.size(); } + + #ifdef TIXML_USE_STL + /// Return the result. + const std::string& Str() { return buffer; } + #endif + +private: + void DoIndent() { + for( int i=0; i +#include + +#include "tinyxml.h" + +//#define DEBUG_PARSER +#if defined( DEBUG_PARSER ) +# if defined( DEBUG ) && defined( _MSC_VER ) +# include +# define TIXML_LOG OutputDebugString +# else +# define TIXML_LOG printf +# endif +#endif + +// Note tha "PutString" hardcodes the same list. This +// is less flexible than it appears. Changing the entries +// or order will break putstring. +TiXmlBase::Entity TiXmlBase::entity[ TiXmlBase::NUM_ENTITY ] = +{ + { "&", 5, '&' }, + { "<", 4, '<' }, + { ">", 4, '>' }, + { """, 6, '\"' }, + { "'", 6, '\'' } +}; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// Including the basic of this table, which determines the #bytes in the +// sequence from the lead byte. 1 placed for invalid sequences -- +// although the result will be junk, pass it through as much as possible. +// Beware of the non-characters in UTF-8: +// ef bb bf (Microsoft "lead bytes") +// ef bf be +// ef bf bf + +const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +const int TiXmlBase::utf8ByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte + 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid +}; + + +void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) + *length = 1; + else if ( input < 0x800 ) + *length = 2; + else if ( input < 0x10000 ) + *length = 3; + else if ( input < 0x200000 ) + *length = 4; + else + { *length = 0; return; } // This code won't covert this correctly anyway. + + output += *length; + + // Scary scary fall throughs. + switch (*length) + { + case 4: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 3: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 2: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 1: + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); + } +} + + +/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalpha( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalpha( anyByte ); +// } +} + + +/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalnum( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalnum( anyByte ); +// } +} + + +class TiXmlParsingData +{ + friend class TiXmlDocument; + public: + void Stamp( const char* now, TiXmlEncoding encoding ); + + const TiXmlCursor& Cursor() const { return cursor; } + + private: + // Only used by the document! + TiXmlParsingData( const char* start, int _tabsize, int row, int col ) + { + assert( start ); + stamp = start; + tabsize = _tabsize; + cursor.row = row; + cursor.col = col; + } + + TiXmlCursor cursor; + const char* stamp; + int tabsize; +}; + + +void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) +{ + assert( now ); + + // Do nothing if the tabsize is 0. + if ( tabsize < 1 ) + { + return; + } + + // Get the current row, column. + int row = cursor.row; + int col = cursor.col; + const char* p = stamp; + assert( p ); + + while ( p < now ) + { + // Treat p as unsigned, so we have a happy compiler. + const unsigned char* pU = (const unsigned char*)p; + + // Code contributed by Fletcher Dunn: (modified by lee) + switch (*pU) { + case 0: + // We *should* never get here, but in case we do, don't + // advance past the terminating null character, ever + return; + + case '\r': + // bump down to the next line + ++row; + col = 0; + // Eat the character + ++p; + + // Check for \r\n sequence, and treat this as a single character + if (*p == '\n') { + ++p; + } + break; + + case '\n': + // bump down to the next line + ++row; + col = 0; + + // Eat the character + ++p; + + // Check for \n\r sequence, and treat this as a single + // character. (Yes, this bizarre thing does occur still + // on some arcane platforms...) + if (*p == '\r') { + ++p; + } + break; + + case '\t': + // Eat the character + ++p; + + // Skip to next tab stop + col = (col / tabsize + 1) * tabsize; + break; + + case TIXML_UTF_LEAD_0: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( *(p+1) && *(p+2) ) + { + // In these cases, don't advance the column. These are + // 0-width spaces. + if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) + p += 3; + else + { p +=3; ++col; } // A normal character. + } + } + else + { + ++p; + ++col; + } + break; + + default: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // Eat the 1 to 4 byte utf8 character. + int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)]; + if ( step == 0 ) + step = 1; // Error case from bad encoding, but handle gracefully. + p += step; + + // Just advance one column, of course. + ++col; + } + else + { + ++p; + ++col; + } + break; + } + } + cursor.row = row; + cursor.col = col; + assert( cursor.row >= -1 ); + assert( cursor.col >= -1 ); + stamp = p; + assert( stamp ); +} + + +const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) +{ + if ( !p || !*p ) + { + return 0; + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + while ( *p ) + { + const unsigned char* pU = (const unsigned char*)p; + + // Skip the stupid Microsoft UTF-8 Byte order marks + if ( *(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==TIXML_UTF_LEAD_1 + && *(pU+2)==TIXML_UTF_LEAD_2 ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbeU ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbfU ) + { + p += 3; + continue; + } + + if ( IsWhiteSpace( *p ) ) // Still using old rules for white space. + ++p; + else + break; + } + } + else + { + while ( *p && IsWhiteSpace( *p ) ) + ++p; + } + + return p; +} + +#ifdef TIXML_USE_STL +/*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ) +{ + for( ;; ) + { + if ( !in->good() ) return false; + + int c = in->peek(); + // At this scope, we can't get to a document. So fail silently. + if ( !IsWhiteSpace( c ) || c <= 0 ) + return true; + + *tag += (char) in->get(); + } +} + +/*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag ) +{ + //assert( character > 0 && character < 128 ); // else it won't work in utf-8 + while ( in->good() ) + { + int c = in->peek(); + if ( c == character ) + return true; + if ( c <= 0 ) // Silent failure: can't get document at this scope + return false; + + in->get(); + *tag += (char) c; + } + return false; +} +#endif + +// One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The +// "assign" optimization removes over 10% of the execution time. +// +const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) +{ + // Oddly, not supported on some comilers, + //name->clear(); + // So use this: + *name = ""; + assert( p ); + + // Names start with letters or underscores. + // Of course, in unicode, tinyxml has no idea what a letter *is*. The + // algorithm is generous. + // + // After that, they can be letters, underscores, numbers, + // hyphens, or colons. (Colons are valid ony for namespaces, + // but tinyxml can't tell namespaces from names.) + if ( p && *p + && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) + { + const char* start = p; + while( p && *p + && ( IsAlphaNum( (unsigned char ) *p, encoding ) + || *p == '_' + || *p == '-' + || *p == '.' + || *p == ':' ) ) + { + //(*name) += *p; // expensive + ++p; + } + if ( p-start > 0 ) { + name->assign( start, p-start ); + } + return p; + } + return 0; +} + +const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) +{ + // Presume an entity, and pull it out. + TIXML_STRING ent; + int i; + *length = 0; + + if ( *(p+1) && *(p+1) == '#' && *(p+2) ) + { + unsigned long ucs = 0; + ptrdiff_t delta = 0; + unsigned mult = 1; + + if ( *(p+2) == 'x' ) + { + // Hexadecimal. + if ( !*(p+3) ) return 0; + + const char* q = p+3; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != 'x' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else if ( *q >= 'a' && *q <= 'f' ) + ucs += mult * (*q - 'a' + 10); + else if ( *q >= 'A' && *q <= 'F' ) + ucs += mult * (*q - 'A' + 10 ); + else + return 0; + mult *= 16; + --q; + } + } + else + { + // Decimal. + if ( !*(p+2) ) return 0; + + const char* q = p+2; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != '#' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else + return 0; + mult *= 10; + --q; + } + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + } + else + { + *value = (char)ucs; + *length = 1; + } + return p + delta + 1; + } + + // Now try to match it. + for( i=0; iappend( cArr, len ); + } + } + else + { + bool whitespace = false; + + // Remove leading white space: + p = SkipWhiteSpace( p, encoding ); + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) ) + { + if ( *p == '\r' || *p == '\n' ) + { + whitespace = true; + ++p; + } + else if ( IsWhiteSpace( *p ) ) + { + whitespace = true; + ++p; + } + else + { + // If we've found whitespace, add it before the + // new character. Any whitespace just becomes a space. + if ( whitespace ) + { + (*text) += ' '; + whitespace = false; + } + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + if ( len == 1 ) + (*text) += cArr[0]; // more efficient + else + text->append( cArr, len ); + } + } + } + if ( p && *p ) + p += strlen( endTag ); + return ( p && *p ) ? p : 0; +} + +#ifdef TIXML_USE_STL + +void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + // The basic issue with a document is that we don't know what we're + // streaming. Read something presumed to be a tag (and hope), then + // identify it, and call the appropriate stream method on the tag. + // + // This "pre-streaming" will never read the closing ">" so the + // sub-tag can orient itself. + + if ( !StreamTo( in, '<', tag ) ) + { + SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + while ( in->good() ) + { + int tagIndex = (int) tag->length(); + while ( in->good() && in->peek() != '>' ) + { + int c = in->get(); + if ( c <= 0 ) + { + SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + break; + } + (*tag) += (char) c; + } + + if ( in->good() ) + { + // We now have something we presume to be a node of + // some sort. Identify it, and call the node to + // continue streaming. + TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); + + if ( node ) + { + node->StreamIn( in, tag ); + bool isElement = node->ToElement() != 0; + delete node; + node = 0; + + // If this is the root element, we're done. Parsing will be + // done by the >> operator. + if ( isElement ) + { + return; + } + } + else + { + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + } + } + // We should have returned sooner. + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); +} + +#endif + +const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) +{ + ClearError(); + + // Parse away, at the document level. Since a document + // contains nothing but other tags, most of what happens + // here is skipping white space. + if ( !p || !*p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + // Note that, for a document, this needs to come + // before the while space skip, so that parsing + // starts from the pointer we are given. + location.Clear(); + if ( prevData ) + { + location.row = prevData->cursor.row; + location.col = prevData->cursor.col; + } + else + { + location.row = 0; + location.col = 0; + } + TiXmlParsingData data( p, TabSize(), location.row, location.col ); + location = data.Cursor(); + + if ( encoding == TIXML_ENCODING_UNKNOWN ) + { + // Check for the Microsoft UTF-8 lead bytes. + const unsigned char* pU = (const unsigned char*)p; + if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 + && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 + && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) + { + encoding = TIXML_ENCODING_UTF8; + useMicrosoftBOM = true; + } + } + + p = SkipWhiteSpace( p, encoding ); + if ( !p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + while ( p && *p ) + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, &data, encoding ); + LinkEndChild( node ); + } + else + { + break; + } + + // Did we get encoding info? + if ( encoding == TIXML_ENCODING_UNKNOWN + && node->ToDeclaration() ) + { + TiXmlDeclaration* dec = node->ToDeclaration(); + const char* enc = dec->Encoding(); + assert( enc ); + + if ( *enc == 0 ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice + else + encoding = TIXML_ENCODING_LEGACY; + } + + p = SkipWhiteSpace( p, encoding ); + } + + // Was this empty? + if ( !firstChild ) { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); + return 0; + } + + // All is well. + return p; +} + +void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + // The first error in a chain is more accurate - don't set again! + if ( error ) + return; + + assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); + error = true; + errorId = err; + errorDesc = errorString[ errorId ]; + + errorLocation.Clear(); + if ( pError && data ) + { + data->Stamp( pError, encoding ); + errorLocation = data->Cursor(); + } +} + + +TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) +{ + TiXmlNode* returnNode = 0; + + p = SkipWhiteSpace( p, encoding ); + if( !p || !*p || *p != '<' ) + { + return 0; + } + + p = SkipWhiteSpace( p, encoding ); + + if ( !p || !*p ) + { + return 0; + } + + // What is this thing? + // - Elements start with a letter or underscore, but xml is reserved. + // - Comments: "; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + if ( document ) + document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // [ 1475201 ] TinyXML parses entities in comments + // Oops - ReadText doesn't work, because we don't want to parse the entities. + // p = ReadText( p, &value, false, endTag, false, encoding ); + // + // from the XML spec: + /* + [Definition: Comments may appear anywhere in a document outside other markup; in addition, + they may appear within the document type declaration at places allowed by the grammar. + They are not part of the document's character data; an XML processor MAY, but need not, + make it possible for an application to retrieve the text of comments. For compatibility, + the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity + references MUST NOT be recognized within comments. + + An example of a comment: + + + */ + + value = ""; + // Keep all the white space. + while ( p && *p && !StringEqual( p, endTag, false, encoding ) ) + { + value.append( p, 1 ); + ++p; + } + if ( p && *p ) + p += strlen( endTag ); + + return p; +} + + +const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) return 0; + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + // Read the name, the '=' and the value. + const char* pErr = p; + p = ReadName( p, &name, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p || *p != '=' ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + ++p; // skip '=' + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + const char* end; + const char SINGLE_QUOTE = '\''; + const char DOUBLE_QUOTE = '\"'; + + if ( *p == SINGLE_QUOTE ) + { + ++p; + end = "\'"; // single quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else if ( *p == DOUBLE_QUOTE ) + { + ++p; + end = "\""; // double quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else + { + // All attribute values should be in single or double quotes. + // But this is such a common error that the parser will try + // its best, even without them. + value = ""; + while ( p && *p // existence + && !IsWhiteSpace( *p ) // whitespace + && *p != '/' && *p != '>' ) // tag end + { + if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) { + // [ 1451649 ] Attribute values with trailing quotes not handled correctly + // We did not have an opening quote but seem to have a + // closing one. Give up and throw an error. + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + value += *p; + ++p; + } + } + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->peek(); + if ( !cdata && (c == '<' ) ) + { + return; + } + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + in->get(); // "commits" the peek made above + + if ( cdata && c == '>' && tag->size() >= 3 ) { + size_t len = tag->size(); + if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) { + // terminator of cdata. + return; + } + } + } +} +#endif + +const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + value = ""; + TiXmlDocument* document = GetDocument(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + + const char* const startTag = ""; + + if ( cdata || StringEqual( p, startTag, false, encoding ) ) + { + cdata = true; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + if ( document ) + document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // Keep all the white space, ignore the encoding, etc. + while ( p && *p + && !StringEqual( p, endTag, false, encoding ) + ) + { + value += *p; + ++p; + } + + TIXML_STRING dummy; + p = ReadText( p, &dummy, false, endTag, false, encoding ); + return p; + } + else + { + bool ignoreWhite = true; + + const char* end = "<"; + p = ReadText( p, &value, ignoreWhite, end, false, encoding ); + if ( p && *p ) + return p-1; // don't truncate the '<' + return 0; + } +} + +#ifdef TIXML_USE_STL +void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + +const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) +{ + p = SkipWhiteSpace( p, _encoding ); + // Find the beginning, find the end, and look for + // the stuff in-between. + TiXmlDocument* document = GetDocument(); + if ( !p || !*p || !StringEqual( p, "SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); + return 0; + } + if ( data ) + { + data->Stamp( p, _encoding ); + location = data->Cursor(); + } + p += 5; + + version = ""; + encoding = ""; + standalone = ""; + + while ( p && *p ) + { + if ( *p == '>' ) + { + ++p; + return p; + } + + p = SkipWhiteSpace( p, _encoding ); + if ( StringEqual( p, "version", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + version = attrib.Value(); + } + else if ( StringEqual( p, "encoding", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + encoding = attrib.Value(); + } + else if ( StringEqual( p, "standalone", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + standalone = attrib.Value(); + } + else + { + // Read over whatever it is. + while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) + ++p; + } + } + return 0; +} + +bool TiXmlText::Blank() const +{ + for ( unsigned i=0; i + #include + using namespace std; +#else + #include +#endif + +#if defined( WIN32 ) && defined( TUNE ) + #include + _CrtMemState startMemState; + _CrtMemState endMemState; +#endif + +#include "tinyxml.h" + +bool XmlTest (const char* testString, const char* expected, const char* found, bool noEcho = false); +bool XmlTest( const char* testString, int expected, int found, bool noEcho = false ); + +static int gPass = 0; +static int gFail = 0; + + + +bool XmlTest (const char* testString, const char* expected, const char* found, bool noEcho ) +{ + bool pass = !strcmp( expected, found ); + if ( pass ) + printf ("[pass]"); + else + printf ("[fail]"); + + if ( noEcho ) + printf (" %s\n", testString); + else + printf (" %s [%s][%s]\n", testString, expected, found); + + if ( pass ) + ++gPass; + else + ++gFail; + return pass; +} + + +bool XmlTest( const char* testString, int expected, int found, bool noEcho ) +{ + bool pass = ( expected == found ); + if ( pass ) + printf ("[pass]"); + else + printf ("[fail]"); + + if ( noEcho ) + printf (" %s\n", testString); + else + printf (" %s [%d][%d]\n", testString, expected, found); + + if ( pass ) + ++gPass; + else + ++gFail; + return pass; +} + + +void NullLineEndings( char* p ) +{ + while( p && *p ) { + if ( *p == '\n' || *p == '\r' ) { + *p = 0; + return; + } + ++p; + } +} + +// +// This file demonstrates some basic functionality of TinyXml. +// Note that the example is very contrived. It presumes you know +// what is in the XML file. But it does test the basic operations, +// and show how to add and remove nodes. +// + +int main() +{ + + // + // We start with the 'demoStart' todo list. Process it. And + // should hopefully end up with the todo list as illustrated. + // + const char* demoStart = + "\n" + "" + "\n" + "\n" + " Go to the Toy store!" + " Do bills " + " Look for Evil Dinosaurs! " + ""; + + { + + #ifdef TIXML_USE_STL + // What the todo list should look like after processing. + // In stream (no formatting) representation. + const char* demoEnd = + "" + "" + "" + "" + "Go to the" + "Toy store!" + "" + "" + "Talk to:" + "" + "" + "" + "" + "" + "" + "Do bills" + "" + ""; + #endif + + // The example parses from the character string (above): + #if defined( WIN32 ) && defined( TUNE ) + _CrtMemCheckpoint( &startMemState ); + #endif + + { + // Write to a file and read it back, to check file I/O. + + TiXmlDocument doc( "demotest.xml" ); + doc.Parse( demoStart ); + + if ( doc.Error() ) + { + printf( "Error in %s: %s\n", doc.Value(), doc.ErrorDesc() ); + exit( 1 ); + } + doc.SaveFile(); + } + + TiXmlDocument doc( "demotest.xml" ); + bool loadOkay = doc.LoadFile(); + + if ( !loadOkay ) + { + printf( "Could not load test file 'demotest.xml'. Error='%s'. Exiting.\n", doc.ErrorDesc() ); + exit( 1 ); + } + + printf( "** Demo doc read from disk: ** \n\n" ); + printf( "** Printing via doc.Print **\n" ); + doc.Print( stdout ); + + { + printf( "** Printing via TiXmlPrinter **\n" ); + TiXmlPrinter printer; + doc.Accept( &printer ); + fprintf( stdout, "%s", printer.CStr() ); + } + #ifdef TIXML_USE_STL + { + printf( "** Printing via operator<< **\n" ); + std::cout << doc; + } + #endif + TiXmlNode* node = 0; + TiXmlElement* todoElement = 0; + TiXmlElement* itemElement = 0; + + + // -------------------------------------------------------- + // An example of changing existing attributes, and removing + // an element from the document. + // -------------------------------------------------------- + + // Get the "ToDo" element. + // It is a child of the document, and can be selected by name. + node = doc.FirstChild( "ToDo" ); + assert( node ); + todoElement = node->ToElement(); + assert( todoElement ); + + // Going to the toy store is now our second priority... + // So set the "priority" attribute of the first item in the list. + node = todoElement->FirstChildElement(); // This skips the "PDA" comment. + assert( node ); + itemElement = node->ToElement(); + assert( itemElement ); + itemElement->SetAttribute( "priority", 2 ); + + // Change the distance to "doing bills" from + // "none" to "here". It's the next sibling element. + itemElement = itemElement->NextSiblingElement(); + assert( itemElement ); + itemElement->SetAttribute( "distance", "here" ); + + // Remove the "Look for Evil Dinosaurs!" item. + // It is 1 more sibling away. We ask the parent to remove + // a particular child. + itemElement = itemElement->NextSiblingElement(); + todoElement->RemoveChild( itemElement ); + + itemElement = 0; + + // -------------------------------------------------------- + // What follows is an example of created elements and text + // nodes and adding them to the document. + // -------------------------------------------------------- + + // Add some meetings. + TiXmlElement item( "Item" ); + item.SetAttribute( "priority", "1" ); + item.SetAttribute( "distance", "far" ); + + TiXmlText text( "Talk to:" ); + + TiXmlElement meeting1( "Meeting" ); + meeting1.SetAttribute( "where", "School" ); + + TiXmlElement meeting2( "Meeting" ); + meeting2.SetAttribute( "where", "Lunch" ); + + TiXmlElement attendee1( "Attendee" ); + attendee1.SetAttribute( "name", "Marple" ); + attendee1.SetAttribute( "position", "teacher" ); + + TiXmlElement attendee2( "Attendee" ); + attendee2.SetAttribute( "name", "Voel" ); + attendee2.SetAttribute( "position", "counselor" ); + + // Assemble the nodes we've created: + meeting1.InsertEndChild( attendee1 ); + meeting1.InsertEndChild( attendee2 ); + + item.InsertEndChild( text ); + item.InsertEndChild( meeting1 ); + item.InsertEndChild( meeting2 ); + + // And add the node to the existing list after the first child. + node = todoElement->FirstChild( "Item" ); + assert( node ); + itemElement = node->ToElement(); + assert( itemElement ); + + todoElement->InsertAfterChild( itemElement, item ); + + printf( "\n** Demo doc processed: ** \n\n" ); + doc.Print( stdout ); + + + #ifdef TIXML_USE_STL + printf( "** Demo doc processed to stream: ** \n\n" ); + cout << doc << endl << endl; + #endif + + // -------------------------------------------------------- + // Different tests...do we have what we expect? + // -------------------------------------------------------- + + int count = 0; + TiXmlElement* element; + + ////////////////////////////////////////////////////// + + #ifdef TIXML_USE_STL + cout << "** Basic structure. **\n"; + ostringstream outputStream( ostringstream::out ); + outputStream << doc; + XmlTest( "Output stream correct.", string( demoEnd ).c_str(), + outputStream.str().c_str(), true ); + #endif + + node = doc.RootElement(); + assert( node ); + XmlTest( "Root element exists.", true, ( node != 0 && node->ToElement() ) ); + XmlTest ( "Root element value is 'ToDo'.", "ToDo", node->Value()); + + node = node->FirstChild(); + XmlTest( "First child exists & is a comment.", true, ( node != 0 && node->ToComment() ) ); + node = node->NextSibling(); + XmlTest( "Sibling element exists & is an element.", true, ( node != 0 && node->ToElement() ) ); + XmlTest ( "Value is 'Item'.", "Item", node->Value() ); + + node = node->FirstChild(); + XmlTest ( "First child exists.", true, ( node != 0 && node->ToText() ) ); + XmlTest ( "Value is 'Go to the'.", "Go to the", node->Value() ); + + + ////////////////////////////////////////////////////// + printf ("\n** Iterators. **\n"); + + // Walk all the top level nodes of the document. + count = 0; + for( node = doc.FirstChild(); + node; + node = node->NextSibling() ) + { + count++; + } + XmlTest( "Top level nodes, using First / Next.", 3, count ); + + count = 0; + for( node = doc.LastChild(); + node; + node = node->PreviousSibling() ) + { + count++; + } + XmlTest( "Top level nodes, using Last / Previous.", 3, count ); + + // Walk all the top level nodes of the document, + // using a different syntax. + count = 0; + for( node = doc.IterateChildren( 0 ); + node; + node = doc.IterateChildren( node ) ) + { + count++; + } + XmlTest( "Top level nodes, using IterateChildren.", 3, count ); + + // Walk all the elements in a node. + count = 0; + for( element = todoElement->FirstChildElement(); + element; + element = element->NextSiblingElement() ) + { + count++; + } + XmlTest( "Children of the 'ToDo' element, using First / Next.", + 3, count ); + + // Walk all the elements in a node by value. + count = 0; + for( node = todoElement->FirstChild( "Item" ); + node; + node = node->NextSibling( "Item" ) ) + { + count++; + } + XmlTest( "'Item' children of the 'ToDo' element, using First/Next.", 3, count ); + + count = 0; + for( node = todoElement->LastChild( "Item" ); + node; + node = node->PreviousSibling( "Item" ) ) + { + count++; + } + XmlTest( "'Item' children of the 'ToDo' element, using Last/Previous.", 3, count ); + + #ifdef TIXML_USE_STL + { + cout << "\n** Parsing. **\n"; + istringstream parse0( "" ); + TiXmlElement element0( "default" ); + parse0 >> element0; + + XmlTest ( "Element parsed, value is 'Element0'.", "Element0", element0.Value() ); + XmlTest ( "Reads attribute 'attribute0=\"foo0\"'.", "foo0", element0.Attribute( "attribute0" )); + XmlTest ( "Reads incorrectly formatted 'attribute1=noquotes'.", "noquotes", element0.Attribute( "attribute1" ) ); + XmlTest ( "Read attribute with entity value '>'.", ">", element0.Attribute( "attribute2" ) ); + } + #endif + + { + const char* error = "\n" + "\n" + " \n" + ""; + + TiXmlDocument docTest; + docTest.Parse( error ); + XmlTest( "Error row", docTest.ErrorRow(), 3 ); + XmlTest( "Error column", docTest.ErrorCol(), 17 ); + //printf( "error=%d id='%s' row %d col%d\n", (int) doc.Error(), doc.ErrorDesc(), doc.ErrorRow()+1, doc.ErrorCol() + 1 ); + + } + + #ifdef TIXML_USE_STL + { + ////////////////////////////////////////////////////// + cout << "\n** Streaming. **\n"; + + // Round trip check: stream in, then stream back out to verify. The stream + // out has already been checked, above. We use the output + + istringstream inputStringStream( outputStream.str() ); + TiXmlDocument document0; + + inputStringStream >> document0; + + ostringstream outputStream0( ostringstream::out ); + outputStream0 << document0; + + XmlTest( "Stream round trip correct.", string( demoEnd ).c_str(), + outputStream0.str().c_str(), true ); + + std::string str; + str << document0; + + XmlTest( "String printing correct.", string( demoEnd ).c_str(), + str.c_str(), true ); + } + #endif + } + + { + const char* str = ""; + + TiXmlDocument doc; + doc.Parse( str ); + + TiXmlElement* ele = doc.FirstChildElement(); + + int iVal, result; + double dVal; + + result = ele->QueryDoubleAttribute( "attr0", &dVal ); + XmlTest( "Query attribute: int as double", result, TIXML_SUCCESS ); + XmlTest( "Query attribute: int as double", (int)dVal, 1 ); + result = ele->QueryDoubleAttribute( "attr1", &dVal ); + XmlTest( "Query attribute: double as double", (int)dVal, 2 ); + result = ele->QueryIntAttribute( "attr1", &iVal ); + XmlTest( "Query attribute: double as int", result, TIXML_SUCCESS ); + XmlTest( "Query attribute: double as int", iVal, 2 ); + result = ele->QueryIntAttribute( "attr2", &iVal ); + XmlTest( "Query attribute: not a number", result, TIXML_WRONG_TYPE ); + result = ele->QueryIntAttribute( "bar", &iVal ); + XmlTest( "Query attribute: does not exist", result, TIXML_NO_ATTRIBUTE ); + } + + { + const char* str = ""; + + TiXmlDocument doc; + doc.Parse( str ); + + TiXmlElement* ele = doc.FirstChildElement(); + + int iVal; + double dVal; + + ele->SetAttribute( "str", "strValue" ); + ele->SetAttribute( "int", 1 ); + ele->SetDoubleAttribute( "double", -1.0 ); + + const char* cStr = ele->Attribute( "str" ); + ele->QueryIntAttribute( "int", &iVal ); + ele->QueryDoubleAttribute( "double", &dVal ); + + XmlTest( "Attribute round trip. c-string.", "strValue", cStr ); + XmlTest( "Attribute round trip. int.", 1, iVal ); + XmlTest( "Attribute round trip. double.", -1, (int)dVal ); + } + + { + const char* str = "\t\t\n" + ""; + + TiXmlDocument doc; + doc.SetTabSize( 8 ); + doc.Parse( str ); + + TiXmlHandle docHandle( &doc ); + TiXmlHandle roomHandle = docHandle.FirstChildElement( "room" ); + + assert( docHandle.Node() ); + assert( roomHandle.Element() ); + + TiXmlElement* room = roomHandle.Element(); + assert( room ); + TiXmlAttribute* doors = room->FirstAttribute(); + assert( doors ); + + XmlTest( "Location tracking: Tab 8: room row", room->Row(), 1 ); + XmlTest( "Location tracking: Tab 8: room col", room->Column(), 49 ); + XmlTest( "Location tracking: Tab 8: doors row", doors->Row(), 1 ); + XmlTest( "Location tracking: Tab 8: doors col", doors->Column(), 55 ); + } + + { + const char* str = "\t\t\n" + " \n" + " A great door!\n" + "\t" + ""; + + TiXmlDocument doc; + doc.Parse( str ); + + TiXmlHandle docHandle( &doc ); + TiXmlHandle roomHandle = docHandle.FirstChildElement( "room" ); + TiXmlHandle commentHandle = docHandle.FirstChildElement( "room" ).FirstChild(); + TiXmlHandle textHandle = docHandle.FirstChildElement( "room" ).ChildElement( "door", 0 ).FirstChild(); + TiXmlHandle door0Handle = docHandle.FirstChildElement( "room" ).ChildElement( 0 ); + TiXmlHandle door1Handle = docHandle.FirstChildElement( "room" ).ChildElement( 1 ); + + assert( docHandle.Node() ); + assert( roomHandle.Element() ); + assert( commentHandle.Node() ); + assert( textHandle.Text() ); + assert( door0Handle.Element() ); + assert( door1Handle.Element() ); + + TiXmlDeclaration* declaration = doc.FirstChild()->ToDeclaration(); + assert( declaration ); + TiXmlElement* room = roomHandle.Element(); + assert( room ); + TiXmlAttribute* doors = room->FirstAttribute(); + assert( doors ); + TiXmlText* text = textHandle.Text(); + TiXmlComment* comment = commentHandle.Node()->ToComment(); + assert( comment ); + TiXmlElement* door0 = door0Handle.Element(); + TiXmlElement* door1 = door1Handle.Element(); + + XmlTest( "Location tracking: Declaration row", declaration->Row(), 1 ); + XmlTest( "Location tracking: Declaration col", declaration->Column(), 5 ); + XmlTest( "Location tracking: room row", room->Row(), 1 ); + XmlTest( "Location tracking: room col", room->Column(), 45 ); + XmlTest( "Location tracking: doors row", doors->Row(), 1 ); + XmlTest( "Location tracking: doors col", doors->Column(), 51 ); + XmlTest( "Location tracking: Comment row", comment->Row(), 2 ); + XmlTest( "Location tracking: Comment col", comment->Column(), 3 ); + XmlTest( "Location tracking: text row", text->Row(), 3 ); + XmlTest( "Location tracking: text col", text->Column(), 24 ); + XmlTest( "Location tracking: door0 row", door0->Row(), 3 ); + XmlTest( "Location tracking: door0 col", door0->Column(), 5 ); + XmlTest( "Location tracking: door1 row", door1->Row(), 4 ); + XmlTest( "Location tracking: door1 col", door1->Column(), 5 ); + } + + + // -------------------------------------------------------- + // UTF-8 testing. It is important to test: + // 1. Making sure name, value, and text read correctly + // 2. Row, Col functionality + // 3. Correct output + // -------------------------------------------------------- + printf ("\n** UTF-8 **\n"); + { + TiXmlDocument doc( "utf8test.xml" ); + doc.LoadFile(); + if ( doc.Error() && doc.ErrorId() == TiXmlBase::TIXML_ERROR_OPENING_FILE ) { + printf( "WARNING: File 'utf8test.xml' not found.\n" + "(Are you running the test from the wrong directory?)\n" + "Could not test UTF-8 functionality.\n" ); + } + else + { + TiXmlHandle docH( &doc ); + // Get the attribute "value" from the "Russian" element and check it. + TiXmlElement* element = docH.FirstChildElement( "document" ).FirstChildElement( "Russian" ).Element(); + const unsigned char correctValue[] = { 0xd1U, 0x86U, 0xd0U, 0xb5U, 0xd0U, 0xbdU, 0xd0U, 0xbdU, + 0xd0U, 0xbeU, 0xd1U, 0x81U, 0xd1U, 0x82U, 0xd1U, 0x8cU, 0 }; + + XmlTest( "UTF-8: Russian value.", (const char*)correctValue, element->Attribute( "value" ), true ); + XmlTest( "UTF-8: Russian value row.", 4, element->Row() ); + XmlTest( "UTF-8: Russian value column.", 5, element->Column() ); + + const unsigned char russianElementName[] = { 0xd0U, 0xa0U, 0xd1U, 0x83U, + 0xd1U, 0x81U, 0xd1U, 0x81U, + 0xd0U, 0xbaU, 0xd0U, 0xb8U, + 0xd0U, 0xb9U, 0 }; + const char russianText[] = "<\xD0\xB8\xD0\xBC\xD0\xB5\xD0\xB5\xD1\x82>"; + + TiXmlText* text = docH.FirstChildElement( "document" ).FirstChildElement( (const char*) russianElementName ).Child( 0 ).Text(); + XmlTest( "UTF-8: Browsing russian element name.", + russianText, + text->Value(), + true ); + XmlTest( "UTF-8: Russian element name row.", 7, text->Row() ); + XmlTest( "UTF-8: Russian element name column.", 47, text->Column() ); + + TiXmlDeclaration* dec = docH.Child( 0 ).Node()->ToDeclaration(); + XmlTest( "UTF-8: Declaration column.", 1, dec->Column() ); + XmlTest( "UTF-8: Document column.", 1, doc.Column() ); + + // Now try for a round trip. + doc.SaveFile( "utf8testout.xml" ); + + // Check the round trip. + char savedBuf[256]; + char verifyBuf[256]; + int okay = 1; + + FILE* saved = fopen( "utf8testout.xml", "r" ); + FILE* verify = fopen( "utf8testverify.xml", "r" ); + + //bool firstLineBOM=true; + if ( saved && verify ) + { + while ( fgets( verifyBuf, 256, verify ) ) + { + fgets( savedBuf, 256, saved ); + NullLineEndings( verifyBuf ); + NullLineEndings( savedBuf ); + + if ( /*!firstLineBOM && */ strcmp( verifyBuf, savedBuf ) ) + { + printf( "verify:%s<\n", verifyBuf ); + printf( "saved :%s<\n", savedBuf ); + okay = 0; + break; + } + //firstLineBOM = false; + } + } + if ( saved ) + fclose( saved ); + if ( verify ) + fclose( verify ); + XmlTest( "UTF-8: Verified multi-language round trip.", 1, okay ); + + // On most Western machines, this is an element that contains + // the word "resume" with the correct accents, in a latin encoding. + // It will be something else completely on non-wester machines, + // which is why TinyXml is switching to UTF-8. + const char latin[] = "r\x82sum\x82"; + + TiXmlDocument latinDoc; + latinDoc.Parse( latin, 0, TIXML_ENCODING_LEGACY ); + + text = latinDoc.FirstChildElement()->FirstChild()->ToText(); + XmlTest( "Legacy encoding: Verify text element.", "r\x82sum\x82", text->Value() ); + } + } + + ////////////////////// + // Copy and assignment + ////////////////////// + printf ("\n** Copy and Assignment **\n"); + { + TiXmlElement element( "foo" ); + element.Parse( "", 0, TIXML_ENCODING_UNKNOWN ); + + TiXmlElement elementCopy( element ); + TiXmlElement elementAssign( "foo" ); + elementAssign.Parse( "", 0, TIXML_ENCODING_UNKNOWN ); + elementAssign = element; + + XmlTest( "Copy/Assign: element copy #1.", "element", elementCopy.Value() ); + XmlTest( "Copy/Assign: element copy #2.", "value", elementCopy.Attribute( "name" ) ); + XmlTest( "Copy/Assign: element assign #1.", "element", elementAssign.Value() ); + XmlTest( "Copy/Assign: element assign #2.", "value", elementAssign.Attribute( "name" ) ); + XmlTest( "Copy/Assign: element assign #3.", true, ( 0 == elementAssign.Attribute( "foo" )) ); + + TiXmlComment comment; + comment.Parse( "", 0, TIXML_ENCODING_UNKNOWN ); + TiXmlComment commentCopy( comment ); + TiXmlComment commentAssign; + commentAssign = commentCopy; + XmlTest( "Copy/Assign: comment copy.", "comment", commentCopy.Value() ); + XmlTest( "Copy/Assign: comment assign.", "comment", commentAssign.Value() ); + + TiXmlUnknown unknown; + unknown.Parse( "<[unknown]>", 0, TIXML_ENCODING_UNKNOWN ); + TiXmlUnknown unknownCopy( unknown ); + TiXmlUnknown unknownAssign; + unknownAssign.Parse( "incorrect", 0, TIXML_ENCODING_UNKNOWN ); + unknownAssign = unknownCopy; + XmlTest( "Copy/Assign: unknown copy.", "[unknown]", unknownCopy.Value() ); + XmlTest( "Copy/Assign: unknown assign.", "[unknown]", unknownAssign.Value() ); + + TiXmlText text( "TextNode" ); + TiXmlText textCopy( text ); + TiXmlText textAssign( "incorrect" ); + textAssign = text; + XmlTest( "Copy/Assign: text copy.", "TextNode", textCopy.Value() ); + XmlTest( "Copy/Assign: text assign.", "TextNode", textAssign.Value() ); + + TiXmlDeclaration dec; + dec.Parse( "", 0, TIXML_ENCODING_UNKNOWN ); + TiXmlDeclaration decCopy( dec ); + TiXmlDeclaration decAssign; + decAssign = dec; + + XmlTest( "Copy/Assign: declaration copy.", "UTF-8", decCopy.Encoding() ); + XmlTest( "Copy/Assign: text assign.", "UTF-8", decAssign.Encoding() ); + + TiXmlDocument doc; + elementCopy.InsertEndChild( textCopy ); + doc.InsertEndChild( decAssign ); + doc.InsertEndChild( elementCopy ); + doc.InsertEndChild( unknownAssign ); + + TiXmlDocument docCopy( doc ); + TiXmlDocument docAssign; + docAssign = docCopy; + + #ifdef TIXML_USE_STL + std::string original, copy, assign; + original << doc; + copy << docCopy; + assign << docAssign; + XmlTest( "Copy/Assign: document copy.", original.c_str(), copy.c_str(), true ); + XmlTest( "Copy/Assign: document assign.", original.c_str(), assign.c_str(), true ); + + #endif + } + + ////////////////////////////////////////////////////// +#ifdef TIXML_USE_STL + printf ("\n** Parsing, no Condense Whitespace **\n"); + TiXmlBase::SetCondenseWhiteSpace( false ); + { + istringstream parse1( "This is \ntext" ); + TiXmlElement text1( "text" ); + parse1 >> text1; + + XmlTest ( "Condense white space OFF.", "This is \ntext", + text1.FirstChild()->Value(), + true ); + } + TiXmlBase::SetCondenseWhiteSpace( true ); +#endif + + ////////////////////////////////////////////////////// + // GetText(); + { + const char* str = "This is text"; + TiXmlDocument doc; + doc.Parse( str ); + const TiXmlElement* element = doc.RootElement(); + + XmlTest( "GetText() normal use.", "This is text", element->GetText() ); + + str = "This is text"; + doc.Clear(); + doc.Parse( str ); + element = doc.RootElement(); + + XmlTest( "GetText() contained element.", element->GetText() == 0, true ); + + str = "This is text"; + doc.Clear(); + TiXmlBase::SetCondenseWhiteSpace( false ); + doc.Parse( str ); + TiXmlBase::SetCondenseWhiteSpace( true ); + element = doc.RootElement(); + + XmlTest( "GetText() partial.", "This is ", element->GetText() ); + } + + + ////////////////////////////////////////////////////// + // CDATA + { + const char* str = "" + " the rules!\n" + "...since I make symbolic puns" + "]]>" + ""; + TiXmlDocument doc; + doc.Parse( str ); + doc.Print(); + + XmlTest( "CDATA parse.", doc.FirstChildElement()->FirstChild()->Value(), + "I am > the rules!\n...since I make symbolic puns", + true ); + + #ifdef TIXML_USE_STL + //cout << doc << '\n'; + + doc.Clear(); + + istringstream parse0( str ); + parse0 >> doc; + //cout << doc << '\n'; + + XmlTest( "CDATA stream.", doc.FirstChildElement()->FirstChild()->Value(), + "I am > the rules!\n...since I make symbolic puns", + true ); + #endif + + TiXmlDocument doc1 = doc; + //doc.Print(); + + XmlTest( "CDATA copy.", doc1.FirstChildElement()->FirstChild()->Value(), + "I am > the rules!\n...since I make symbolic puns", + true ); + } + { + // [ 1482728 ] Wrong wide char parsing + char buf[256]; + buf[255] = 0; + for( int i=0; i<255; ++i ) { + buf[i] = (char)((i>=32) ? i : 32); + } + TIXML_STRING str( ""; + + TiXmlDocument doc; + doc.Parse( str.c_str() ); + + TiXmlPrinter printer; + printer.SetStreamPrinting(); + doc.Accept( &printer ); + + XmlTest( "CDATA with all bytes #1.", str.c_str(), printer.CStr(), true ); + + #ifdef TIXML_USE_STL + doc.Clear(); + istringstream iss( printer.Str() ); + iss >> doc; + std::string out; + out << doc; + XmlTest( "CDATA with all bytes #2.", out.c_str(), printer.CStr(), true ); + #endif + } + { + // [ 1480107 ] Bug-fix for STL-streaming of CDATA that contains tags + // CDATA streaming had a couple of bugs, that this tests for. + const char* str = "" + "I am > the rules!\n" + "...since I make symbolic puns" + "]]>" + ""; + TiXmlDocument doc; + doc.Parse( str ); + doc.Print(); + + XmlTest( "CDATA parse. [ 1480107 ]", doc.FirstChildElement()->FirstChild()->Value(), + "I am > the rules!\n...since I make symbolic puns", + true ); + + #ifdef TIXML_USE_STL + + doc.Clear(); + + istringstream parse0( str ); + parse0 >> doc; + + XmlTest( "CDATA stream. [ 1480107 ]", doc.FirstChildElement()->FirstChild()->Value(), + "I am > the rules!\n...since I make symbolic puns", + true ); + #endif + + TiXmlDocument doc1 = doc; + //doc.Print(); + + XmlTest( "CDATA copy. [ 1480107 ]", doc1.FirstChildElement()->FirstChild()->Value(), + "I am > the rules!\n...since I make symbolic puns", + true ); + } + ////////////////////////////////////////////////////// + // Visit() + + + + ////////////////////////////////////////////////////// + printf( "\n** Fuzzing... **\n" ); + + const int FUZZ_ITERATION = 300; + + // The only goal is not to crash on bad input. + int len = (int) strlen( demoStart ); + for( int i=0; i" + "" + " " + ""; + + TiXmlDocument doc( "passages.xml" ); + doc.Parse( passages ); + TiXmlElement* psg = doc.RootElement()->FirstChildElement(); + const char* context = psg->Attribute( "context" ); + const char* expected = "Line 5 has \"quotation marks\" and 'apostrophe marks'. It also has <, >, and &, as well as a fake copyright \xC2\xA9."; + + XmlTest( "Entity transformation: read. ", expected, context, true ); + + FILE* textfile = fopen( "textfile.txt", "w" ); + if ( textfile ) + { + psg->Print( textfile, 0 ); + fclose( textfile ); + } + textfile = fopen( "textfile.txt", "r" ); + assert( textfile ); + if ( textfile ) + { + char buf[ 1024 ]; + fgets( buf, 1024, textfile ); + XmlTest( "Entity transformation: write. ", + "", + buf, + true ); + } + fclose( textfile ); + } + + { + FILE* textfile = fopen( "test5.xml", "w" ); + if ( textfile ) + { + fputs("", textfile); + fclose(textfile); + + TiXmlDocument doc; + doc.LoadFile( "test5.xml" ); + XmlTest( "dot in element attributes and names", doc.Error(), 0); + } + } + + { + FILE* textfile = fopen( "test6.xml", "w" ); + if ( textfile ) + { + fputs("1.1 Start easy ignore fin thickness ", textfile ); + fclose(textfile); + + TiXmlDocument doc; + bool result = doc.LoadFile( "test6.xml" ); + XmlTest( "Entity with one digit.", result, true ); + + TiXmlText* text = doc.FirstChildElement()->FirstChildElement()->FirstChild()->ToText(); + XmlTest( "Entity with one digit.", + text->Value(), "1.1 Start easy ignore fin thickness\n" ); + } + } + + { + // DOCTYPE not preserved (950171) + // + const char* doctype = + "" + "" + "" + "" + ""; + + TiXmlDocument doc; + doc.Parse( doctype ); + doc.SaveFile( "test7.xml" ); + doc.Clear(); + doc.LoadFile( "test7.xml" ); + + TiXmlHandle docH( &doc ); + TiXmlUnknown* unknown = docH.Child( 1 ).Unknown(); + XmlTest( "Correct value of unknown.", "!DOCTYPE PLAY SYSTEM 'play.dtd'", unknown->Value() ); + #ifdef TIXML_USE_STL + TiXmlNode* node = docH.Child( 2 ).Node(); + std::string str; + str << (*node); + XmlTest( "Correct streaming of unknown.", "", str.c_str() ); + #endif + } + + { + // [ 791411 ] Formatting bug + // Comments do not stream out correctly. + const char* doctype = + ""; + TiXmlDocument doc; + doc.Parse( doctype ); + + TiXmlHandle docH( &doc ); + TiXmlComment* comment = docH.Child( 0 ).Node()->ToComment(); + + XmlTest( "Comment formatting.", " Somewhat ", comment->Value() ); + #ifdef TIXML_USE_STL + std::string str; + str << (*comment); + XmlTest( "Comment streaming.", "", str.c_str() ); + #endif + } + + { + // [ 870502 ] White space issues + TiXmlDocument doc; + TiXmlText* text; + TiXmlHandle docH( &doc ); + + const char* doctype0 = " This has leading and trailing space "; + const char* doctype1 = "This has internal space"; + const char* doctype2 = " This has leading, trailing, and internal space "; + + TiXmlBase::SetCondenseWhiteSpace( false ); + doc.Clear(); + doc.Parse( doctype0 ); + text = docH.FirstChildElement( "element" ).Child( 0 ).Text(); + XmlTest( "White space kept.", " This has leading and trailing space ", text->Value() ); + + doc.Clear(); + doc.Parse( doctype1 ); + text = docH.FirstChildElement( "element" ).Child( 0 ).Text(); + XmlTest( "White space kept.", "This has internal space", text->Value() ); + + doc.Clear(); + doc.Parse( doctype2 ); + text = docH.FirstChildElement( "element" ).Child( 0 ).Text(); + XmlTest( "White space kept.", " This has leading, trailing, and internal space ", text->Value() ); + + TiXmlBase::SetCondenseWhiteSpace( true ); + doc.Clear(); + doc.Parse( doctype0 ); + text = docH.FirstChildElement( "element" ).Child( 0 ).Text(); + XmlTest( "White space condensed.", "This has leading and trailing space", text->Value() ); + + doc.Clear(); + doc.Parse( doctype1 ); + text = docH.FirstChildElement( "element" ).Child( 0 ).Text(); + XmlTest( "White space condensed.", "This has internal space", text->Value() ); + + doc.Clear(); + doc.Parse( doctype2 ); + text = docH.FirstChildElement( "element" ).Child( 0 ).Text(); + XmlTest( "White space condensed.", "This has leading, trailing, and internal space", text->Value() ); + } + + { + // Double attributes + const char* doctype = ""; + + TiXmlDocument doc; + doc.Parse( doctype ); + + XmlTest( "Parsing repeated attributes.", true, doc.Error() ); // is an error to tinyxml (didn't use to be, but caused issues) + //XmlTest( "Parsing repeated attributes.", "blue", doc.FirstChildElement( "element" )->Attribute( "attr" ) ); + } + + { + // Embedded null in stream. + const char* doctype = ""; + + TiXmlDocument doc; + doc.Parse( doctype ); + XmlTest( "Embedded null throws error.", true, doc.Error() ); + + #ifdef TIXML_USE_STL + istringstream strm( doctype ); + doc.Clear(); + doc.ClearError(); + strm >> doc; + XmlTest( "Embedded null throws error.", true, doc.Error() ); + #endif + } + + { + // Legacy mode test. (This test may only pass on a western system) + const char* str = + "" + "<ä>" + "CöntäntßäöüÄÖÜ" + ""; + + TiXmlDocument doc; + doc.Parse( str ); + + TiXmlHandle docHandle( &doc ); + TiXmlHandle aHandle = docHandle.FirstChildElement( "ä" ); + TiXmlHandle tHandle = aHandle.Child( 0 ); + assert( aHandle.Element() ); + assert( tHandle.Text() ); + XmlTest( "ISO-8859-1 Parsing.", "CöntäntßäöüÄÖÜ", tHandle.Text()->Value() ); + } + + { + // Empty documents should return TIXML_ERROR_PARSING_EMPTY, bug 1070717 + const char* str = " "; + TiXmlDocument doc; + doc.Parse( str ); + XmlTest( "Empty document error TIXML_ERROR_DOCUMENT_EMPTY", TiXmlBase::TIXML_ERROR_DOCUMENT_EMPTY, doc.ErrorId() ); + } + #ifndef TIXML_USE_STL + { + // String equality. [ 1006409 ] string operator==/!= no worky in all cases + TiXmlString temp; + XmlTest( "Empty tinyxml string compare equal", ( temp == "" ), true ); + + TiXmlString foo; + TiXmlString bar( "" ); + XmlTest( "Empty tinyxml string compare equal", ( foo == bar ), true ); + } + + #endif + { + // Bug [ 1195696 ] from marlonism + TiXmlBase::SetCondenseWhiteSpace(false); + TiXmlDocument xml; + xml.Parse("This hangs"); + XmlTest( "Test safe error return.", xml.Error(), false ); + } + + { + // Bug [ 1243992 ] - another infinite loop + TiXmlDocument doc; + doc.SetCondenseWhiteSpace(false); + doc.Parse("

test

"); + } + { + // Low entities + TiXmlDocument xml; + xml.Parse( "" ); + const char result[] = { 0x0e, 0 }; + XmlTest( "Low entities.", xml.FirstChildElement()->GetText(), result ); + xml.Print(); + } + { + // Bug [ 1451649 ] Attribute values with trailing quotes not handled correctly + TiXmlDocument xml; + xml.Parse( "" ); + XmlTest( "Throw error with bad end quotes.", xml.Error(), true ); + } + #ifdef TIXML_USE_STL + { + // Bug [ 1449463 ] Consider generic query + TiXmlDocument xml; + xml.Parse( "" ); + + TiXmlElement* ele = xml.FirstChildElement(); + double d; + int i; + float f; + bool b; + std::string str; + + XmlTest( "QueryValueAttribute", ele->QueryValueAttribute( "bar", &d ), TIXML_SUCCESS ); + XmlTest( "QueryValueAttribute", ele->QueryValueAttribute( "bar", &i ), TIXML_SUCCESS ); + XmlTest( "QueryValueAttribute", ele->QueryValueAttribute( "bar", &f ), TIXML_SUCCESS ); + XmlTest( "QueryValueAttribute", ele->QueryValueAttribute( "bar", &b ), TIXML_WRONG_TYPE ); + XmlTest( "QueryValueAttribute", ele->QueryValueAttribute( "nobar", &b ), TIXML_NO_ATTRIBUTE ); + XmlTest( "QueryValueAttribute", ele->QueryValueAttribute( "barStr", &str ), TIXML_SUCCESS ); + + XmlTest( "QueryValueAttribute", (d==3.0), true ); + XmlTest( "QueryValueAttribute", (i==3), true ); + XmlTest( "QueryValueAttribute", (f==3.0f), true ); + XmlTest( "QueryValueAttribute", (str==std::string( "a string" )), true ); + } + #endif + + #ifdef TIXML_USE_STL + { + // [ 1505267 ] redundant malloc in TiXmlElement::Attribute + TiXmlDocument xml; + xml.Parse( "" ); + TiXmlElement* ele = xml.FirstChildElement(); + double d; + int i; + + std::string bar = "bar"; + + const std::string* atrrib = ele->Attribute( bar ); + ele->Attribute( bar, &d ); + ele->Attribute( bar, &i ); + + XmlTest( "Attribute", atrrib->empty(), false ); + XmlTest( "Attribute", (d==3.0), true ); + XmlTest( "Attribute", (i==3), true ); + } + #endif + + { + // [ 1356059 ] Allow TiXMLDocument to only be at the top level + TiXmlDocument xml, xml2; + xml.InsertEndChild( xml2 ); + XmlTest( "Document only at top level.", xml.Error(), true ); + XmlTest( "Document only at top level.", xml.ErrorId(), TiXmlBase::TIXML_ERROR_DOCUMENT_TOP_ONLY ); + } + + { + // [ 1663758 ] Failure to report error on bad XML + TiXmlDocument xml; + xml.Parse(""); + XmlTest("Missing end tag at end of input", xml.Error(), true); + xml.Parse(" "); + XmlTest("Missing end tag with trailing whitespace", xml.Error(), true); + } + + { + // [ 1635701 ] fail to parse files with a tag separated into two lines + // I'm not sure this is a bug. Marked 'pending' for feedback. + TiXmlDocument xml; + xml.Parse( "<p>text</p\n><title>" ); + //xml.Print(); + //XmlTest( "Tag split by newline", xml.Error(), false ); + } + + #ifdef TIXML_USE_STL + { + // [ 1475201 ] TinyXML parses entities in comments + TiXmlDocument xml; + istringstream parse1( "<!-- declarations for <head> & <body> -->" + "<!-- far & away -->" ); + parse1 >> xml; + + TiXmlNode* e0 = xml.FirstChild(); + TiXmlNode* e1 = e0->NextSibling(); + TiXmlComment* c0 = e0->ToComment(); + TiXmlComment* c1 = e1->ToComment(); + + XmlTest( "Comments ignore entities.", " declarations for <head> & <body> ", c0->Value(), true ); + XmlTest( "Comments ignore entities.", " far & away ", c1->Value(), true ); + } + #endif + + { + // [ 1475201 ] TinyXML parses entities in comments + TiXmlDocument xml; + xml.Parse("<!-- declarations for <head> & <body> -->" + "<!-- far & away -->" ); + + TiXmlNode* e0 = xml.FirstChild(); + TiXmlNode* e1 = e0->NextSibling(); + TiXmlComment* c0 = e0->ToComment(); + TiXmlComment* c1 = e1->ToComment(); + + XmlTest( "Comments ignore entities.", " declarations for <head> & <body> ", c0->Value(), true ); + XmlTest( "Comments ignore entities.", " far & away ", c1->Value(), true ); + } + + { + TiXmlDocument xml; + xml.Parse( "<Parent>" + "<child1 att=''/>" + "<!-- With this comment, child2 will not be parsed! -->" + "<child2 att=''/>" + "</Parent>" ); + int count = 0; + + TiXmlNode* ele = 0; + while ( (ele = xml.FirstChildElement( "Parent" )->IterateChildren( ele ) ) != 0 ) { + ++count; + } + XmlTest( "Comments iterate correctly.", 3, count ); + } + + { + // trying to repro ]1874301]. If it doesn't go into an infinite loop, all is well. + unsigned char buf[] = "<?xml version=\"1.0\" encoding=\"utf-8\"?><feed><![CDATA[Test XMLblablablalblbl"; + buf[60] = 239; + buf[61] = 0; + + TiXmlDocument doc; + doc.Parse( (const char*)buf); + } + + + { + // bug 1827248 Error while parsing a little bit malformed file + // Actually not malformed - should work. + TiXmlDocument xml; + xml.Parse( "<attributelist> </attributelist >" ); + XmlTest( "Handle end tag whitespace", false, xml.Error() ); + } + + { + // This one must not result in an infinite loop + TiXmlDocument xml; + xml.Parse( "<infinite>loop" ); + XmlTest( "Infinite loop test.", true, true ); + } + + { + // 1709904 - can not repro the crash + { + TiXmlDocument xml; + xml.Parse( "<tag>/</tag>" ); + XmlTest( "Odd XML parsing.", xml.FirstChild()->Value(), "tag" ); + } + /* Could not repro. { + TiXmlDocument xml; + xml.LoadFile( "EQUI_Inventory.xml" ); + //XmlTest( "Odd XML parsing.", xml.FirstChildElement()->Value(), "XML" ); + TiXmlPrinter printer; + xml.Accept( &printer ); + fprintf( stdout, "%s", printer.CStr() ); + }*/ + } + + /* 1417717 experiment + { + TiXmlDocument xml; + xml.Parse("<text>Dan & Tracie</text>"); + xml.Print(stdout); + } + { + TiXmlDocument xml; + xml.Parse("<text>Dan &foo; Tracie</text>"); + xml.Print(stdout); + } + */ + + #if defined( WIN32 ) && defined( TUNE ) + _CrtMemCheckpoint( &endMemState ); + //_CrtMemDumpStatistics( &endMemState ); + + _CrtMemState diffMemState; + _CrtMemDifference( &diffMemState, &startMemState, &endMemState ); + _CrtMemDumpStatistics( &diffMemState ); + #endif + + printf ("\nPass %d, Fail %d\n", gPass, gFail); + return gFail; +} diff --git a/opm/core/utility/test/Makefile.am b/opm/core/utility/test/Makefile.am new file mode 100644 index 00000000..1d20fee8 --- /dev/null +++ b/opm/core/utility/test/Makefile.am @@ -0,0 +1,29 @@ +# $Date$ +# $Revision$ + +check_PROGRAMS = sparsetable_test sparsevector_test monotcubicinterpolator_test +noinst_PROGRAMS = unit_test + +sparsetable_test_SOURCES = sparsetable_test.cpp +sparsetable_test_CXXFLAGS = $(DUNEMPICPPFLAGS) $(BOOST_CPPFLAGS) +sparsetable_test_LDADD = $(DUNE_LDFLAGS) $(DUNEMPILDFLAGS) $(BOOST_LDFLAGS) \ + $(DUNE_LIBS) $(DUNEMPILIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) + +sparsevector_test_SOURCES = sparsevector_test.cpp +sparsevector_test_CXXFLAGS = $(DUNEMPICPPFLAGS) $(BOOST_CPPFLAGS) +sparsevector_test_LDADD = $(DUNE_LDFLAGS) $(DUNEMPILDFLAGS) $(BOOST_LDFLAGS) \ + $(DUNE_LIBS) $(DUNEMPILIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) + +monotcubicinterpolator_test_SOURCES = monotcubicinterpolator_test.cpp +monotcubicinterpolator_test_CXXFLAGS = $(DUNEMPICPPFLAGS) $(BOOST_CPPFLAGS) +monotcubicinterpolator_test_LDADD = $(DUNE_LDFLAGS) $(DUNEMPILDFLAGS) \ + $(DUNE_LIBS) $(DUNEMPILIBS) ../libcommon.la + +unit_test_SOURCES = unit_test.cpp +unit_test_CXXFLAGS = $(DUNEMPICPPFLAGS) $(BOOST_CPPFLAGS) +unit_test_LDADD = $(DUNE_LDFLAGS) $(DUNEMPILDFLAGS) $(DUNE_LIBS) $(DUNEMPILIBS) + +TESTS = $(check_PROGRAMS) + +include $(top_srcdir)/am/global-rules + diff --git a/opm/core/utility/test/monotcubicinterpolator_test.cpp b/opm/core/utility/test/monotcubicinterpolator_test.cpp new file mode 100644 index 00000000..7e7a86d7 --- /dev/null +++ b/opm/core/utility/test/monotcubicinterpolator_test.cpp @@ -0,0 +1,53 @@ +//=========================================================================== +// +// File: monotcubicinterpolator_test.cpp +// +// Created: Tue Dec 8 12:25:30 2009 +// +// Author(s): Atgeirr F Rasmussen <atgeirr@sintef.no> +// Bård Skaflestad <bard.skaflestad@sintef.no> +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* + Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. + Copyright 2009, 2010 Statoil ASA. + + This file is part of The Open Reservoir Simulator Project (OpenRS). + + OpenRS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenRS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenRS. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <dune/common/MonotCubicInterpolator.hpp> + +int main() +{ + const int num_v = 3; + double xv[num_v] = {0.0, 1.0, 2.0}; + double fv[num_v] = {10.0, 21.0, 2.0}; + std::vector<double> x(xv, xv + num_v); + std::vector<double> f(fv, fv + num_v); + MonotCubicInterpolator interp(x, f); + interp.evaluate(-1.0); + interp.evaluate(0.0); + interp.evaluate(0.0001); + interp.evaluate(0.5); + interp.evaluate(1.0); + interp.evaluate(2.0); + interp.evaluate(4.0); +} diff --git a/opm/core/utility/test/sparsetable_test.cpp b/opm/core/utility/test/sparsetable_test.cpp new file mode 100644 index 00000000..76bc7938 --- /dev/null +++ b/opm/core/utility/test/sparsetable_test.cpp @@ -0,0 +1,128 @@ +//=========================================================================== +// +// File: test_sparsetable.cpp +// +// Created: Thu May 28 10:01:46 2009 +// +// Author(s): Atgeirr F Rasmussen <atgeirr@sintef.no> +// Bård Skaflestad <bard.skaflestad@sintef.no> +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* + Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. + Copyright 2009, 2010 Statoil ASA. + + This file is part of The Open Reservoir Simulator Project (OpenRS). + + OpenRS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenRS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenRS. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define BOOST_TEST_DYN_LINK +#define NVERBOSE // to suppress our messages when throwing + +#define BOOST_TEST_MODULE SparseTableTest +#include <boost/test/unit_test.hpp> + +#include "../SparseTable.hpp" + +using namespace Dune; + +BOOST_AUTO_TEST_CASE(construction_and_queries) +{ + const SparseTable<int> st1; + BOOST_CHECK(st1.empty()); + BOOST_CHECK_EQUAL(st1.size(), 0); + BOOST_CHECK_EQUAL(st1.dataSize(), 0); + + // This should be getting us a table like this: + // ---------------- + // 0 + // <empty row> + // 1 2 + // 3 4 5 6 + // 7 8 9 + // ---------------- + const int num_elem = 10; + const int elem[num_elem] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + const int num_rows = 5; + const int rowsizes[num_rows] = { 1, 0, 2, 4, 3 }; + const SparseTable<int> st2(elem, elem + num_elem, rowsizes, rowsizes + num_rows); + BOOST_CHECK(!st2.empty()); + BOOST_CHECK_EQUAL(st2.size(), num_rows); + BOOST_CHECK_EQUAL(st2.dataSize(), num_elem); + BOOST_CHECK_EQUAL(st2[0][0], 0); + BOOST_CHECK_EQUAL(st2.rowSize(0), 1); + BOOST_CHECK(st2[1].empty()); + BOOST_CHECK_EQUAL(st2.rowSize(1), 0); + BOOST_CHECK_EQUAL(st2[3][1], 4); + BOOST_CHECK_EQUAL(st2[4][2], 9); + BOOST_CHECK(st2[4].size() == rowsizes[4]); + const SparseTable<int> st2_again(elem, elem + num_elem, rowsizes, rowsizes + num_rows); + BOOST_CHECK(st2 == st2_again); + SparseTable<int> st2_byassign; + st2_byassign.assign(elem, elem + num_elem, rowsizes, rowsizes + num_rows); + BOOST_CHECK(st2 == st2_byassign); + const int last_row_size = rowsizes[num_rows - 1]; + SparseTable<int> st2_append(elem, elem + num_elem - last_row_size, rowsizes, rowsizes + num_rows - 1); + BOOST_CHECK_EQUAL(st2_append.dataSize(), num_elem - last_row_size); + st2_append.appendRow(elem + num_elem - last_row_size, elem + num_elem); + BOOST_CHECK(st2 == st2_append); + SparseTable<int> st2_append2; + st2_append2.appendRow(elem, elem + 1); + st2_append2.appendRow(elem + 1, elem + 1); + st2_append2.appendRow(elem + 1, elem + 3); + st2_append2.appendRow(elem + 3, elem + 7); + st2_append2.appendRow(elem + 7, elem + 10); + BOOST_CHECK(st2 == st2_append2); + st2_append2.clear(); + SparseTable<int> st_empty; + BOOST_CHECK(st2_append2 == st_empty); + + SparseTable<int> st2_allocate; + st2_allocate.allocate(rowsizes, rowsizes + num_rows); + BOOST_CHECK_EQUAL(st2_allocate.size(), num_rows); + BOOST_CHECK_EQUAL(st2_allocate.dataSize(), num_elem); + int s = 0; + for (int i = 0; i < num_rows; ++i) { + SparseTable<int>::mutable_row_type row = st2_allocate[i]; + for (int j = 0; j < rowsizes[i]; ++j, ++s) + row[j] = elem[s]; + } + BOOST_CHECK(st2 == st2_allocate); + + // One element too few. + BOOST_CHECK_THROW(const SparseTable<int> st3(elem, elem + num_elem - 1, rowsizes, rowsizes + num_rows), std::exception); + + // A few elements too many. + BOOST_CHECK_THROW(const SparseTable<int> st4(elem, elem + num_elem, rowsizes, rowsizes + num_rows - 1), std::exception); + + // Need at least one row. + BOOST_CHECK_THROW(const SparseTable<int> st5(elem, elem + num_elem, rowsizes, rowsizes), std::exception); + + // Tests that only run in debug mode. +#ifndef NDEBUG + // Do not ask for wrong row numbers. + BOOST_CHECK_THROW(st1.rowSize(0), std::exception); + BOOST_CHECK_THROW(st2.rowSize(-1), std::exception); + BOOST_CHECK_THROW(st2.rowSize(st2.size()), std::exception); + // No negative row sizes. + const int err_rs[num_rows] = { 1, 0, -1, 7, 3 }; + BOOST_CHECK_THROW(const SparseTable<int> st6(elem, elem + num_elem, err_rs, err_rs + num_rows), std::exception); +#endif +} diff --git a/opm/core/utility/test/sparsevector_test.cpp b/opm/core/utility/test/sparsevector_test.cpp new file mode 100644 index 00000000..e6ec98bf --- /dev/null +++ b/opm/core/utility/test/sparsevector_test.cpp @@ -0,0 +1,106 @@ +//=========================================================================== +// +// File: sparsevector_test.cpp +// +// Created: Mon Jun 29 21:00:53 2009 +// +// Author(s): Atgeirr F Rasmussen <atgeirr@sintef.no> +// Bård Skaflestad <bard.skaflestad@sintef.no> +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* + Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. + Copyright 2009, 2010 Statoil ASA. + + This file is part of The Open Reservoir Simulator Project (OpenRS). + + OpenRS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenRS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenRS. If not, see <http://www.gnu.org/licenses/>. +*/ + + +#define BOOST_TEST_DYN_LINK +#define NVERBOSE // to suppress our messages when throwing + +#define BOOST_TEST_MODULE SparseVectorTest +#include <boost/test/unit_test.hpp> + +#include "../SparseVector.hpp" + +using namespace Dune; + +BOOST_AUTO_TEST_CASE(construction_and_queries) +{ + const SparseVector<int> sv1; + BOOST_CHECK(sv1.empty()); + BOOST_CHECK_EQUAL(sv1.size(), 0); + BOOST_CHECK_EQUAL(sv1.nonzeroSize(), 0); + + const int size = 100; + const int num_elem = 9; + const int elem[num_elem] = { 9, 8, 7, 6, 5, 4, 3, 2, 1 }; + const int indices[num_elem] = { 1, 2, 3, 5, 8, 13, 21, 34, 55 }; + const SparseVector<int> sv2(size, elem, elem + num_elem, indices, indices + num_elem); + BOOST_CHECK(!sv2.empty()); + BOOST_CHECK_EQUAL(sv2.size(), size); + BOOST_CHECK_EQUAL(sv2.element(0), 0); + BOOST_CHECK_EQUAL(sv2.element(1), 9); + BOOST_CHECK_EQUAL(sv2.element(2), 8); + BOOST_CHECK_EQUAL(sv2.element(3), 7); + BOOST_CHECK_EQUAL(sv2.element(4), 0); + BOOST_CHECK_EQUAL(sv2.element(5), 6); + BOOST_CHECK_EQUAL(sv2.element(55), 1); + BOOST_CHECK_EQUAL(sv2.element(99), 0); + BOOST_CHECK_EQUAL(sv2.nonzeroSize(), num_elem); + for (int i = 0; i < num_elem; ++i) { + BOOST_CHECK_EQUAL(sv2.nonzeroElement(i), elem[i]); + } + const SparseVector<int> sv2_again(size, elem, elem + num_elem, indices, indices + num_elem); + BOOST_CHECK(sv2 == sv2_again); + SparseVector<int> sv2_append(size, elem, elem + num_elem - 1, indices, indices + num_elem - 1); + BOOST_CHECK_EQUAL(sv2_append.nonzeroSize(), num_elem - 1); + sv2_append.addElement(elem[num_elem - 1], indices[num_elem - 1]); + BOOST_CHECK(sv2 == sv2_append); + SparseVector<int> sv2_append2(size); + for (int i = 0; i < num_elem; ++i) { + sv2_append2.addElement(elem[i], indices[i]); + } + BOOST_CHECK(sv2 == sv2_append2); + sv2_append2.clear(); + SparseVector<int> sv_empty; + BOOST_CHECK(sv2_append2 == sv_empty); + + // Tests that only run in debug mode. +#ifndef NDEBUG + // One element too few. + BOOST_CHECK_THROW(const SparseVector<int> sv3(size, elem, elem + num_elem - 1, indices, indices + num_elem), std::exception); + // One element too many. + BOOST_CHECK_THROW(const SparseVector<int> sv4(size, elem, elem + num_elem, indices, indices + num_elem - 1), std::exception); + // Indices out of range. + BOOST_CHECK_THROW(const SparseVector<int> sv5(4, elem, elem + num_elem, indices, indices + num_elem), std::exception); + // Indices not strictly increasing. Cheating by using the elements as indices. + BOOST_CHECK_THROW(const SparseVector<int> sv5(size, elem, elem + num_elem, elem, elem + num_elem), std::exception); + + // Do not ask for out of range indices. + BOOST_CHECK_THROW(sv1.element(0), std::exception); + BOOST_CHECK_THROW(sv2.element(-1), std::exception); + BOOST_CHECK_THROW(sv2.element(sv2.size()), std::exception); + BOOST_CHECK_THROW(sv2.nonzeroElement(sv2.nonzeroSize()), std::exception); +#endif +} + diff --git a/opm/core/utility/test/unit_test.cpp b/opm/core/utility/test/unit_test.cpp new file mode 100644 index 00000000..5904236b --- /dev/null +++ b/opm/core/utility/test/unit_test.cpp @@ -0,0 +1,78 @@ +//=========================================================================== +// +// File: unit_test.cpp +// +// Created: Fri Jul 17 11:02:33 2009 +// +// Author(s): Bård Skaflestad <bard.skaflestad@sintef.no> +// Atgeirr F Rasmussen <atgeirr@sintef.no> +// +// $Date$ +// +// $Revision$ +// +//=========================================================================== + +/* + Copyright 2009, 2010 SINTEF ICT, Applied Mathematics. + Copyright 2009, 2010 Statoil ASA. + + This file is part of The Open Reservoir Simulator Project (OpenRS). + + OpenRS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenRS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenRS. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <algorithm> +#include <iostream> +#include <iterator> +#include <vector> + +#include <boost/bind.hpp> + +#include "../Units.hpp" + +using namespace Dune::prefix; +using namespace Dune::unit; + +int main() +{ + std::cout << "m = " << meter << '\n'; + std::cout << "kg = " << kilogram << '\n'; + std::cout << "s = " << second << '\n'; + + std::cout << "mD = " << milli*darcy << '\n'; + std::cout << "MD = " << mega*darcy << '\n'; + + std::cout << "MD = " << convert::to(mega*darcy, milli*darcy) << " mD\n"; + + std::cout << "1 bar = " << convert::to(convert::from(1.0, barsa), psia) << " psi\n"; + + std::cout << "1 atm = " << convert::to(1*atm, barsa) << " bar\n"; + + std::vector<double> flux(10, 10000*cubic(meter)/year); + + std::cout << "flux = ["; + std::copy(flux.begin(), flux.end(), + std::ostream_iterator<double>(std::cout, " ")); + std::cout << "\b] (m^3/s)\n"; + + std::transform(flux.begin(), flux.end(), flux.begin(), + boost::bind(convert::to, _1, cubic(meter)/year)); + std::cout << " = ["; + std::copy(flux.begin(), flux.end(), + std::ostream_iterator<double>(std::cout, " ")); + std::cout << "\b] (m^3/year)\n"; + + return 0; +}