43 #include "input_output/FGXMLElement.h"
53 FGTable::FGTable(
int NRows)
54 : nRows(NRows), nCols(1)
58 Data.push_back(std::numeric_limits<double>::quiet_NaN());
59 Data.push_back(std::numeric_limits<double>::quiet_NaN());
65 FGTable::FGTable(
int NRows,
int NCols)
66 : nRows(NRows), nCols(NCols)
70 Data.push_back(std::numeric_limits<double>::quiet_NaN());
77 : PropertyManager(t.PropertyManager)
82 internal = t.internal;
84 lookupProperty[0] = t.lookupProperty[0];
85 lookupProperty[1] = t.lookupProperty[1];
86 lookupProperty[2] = t.lookupProperty[2];
89 Tables.reserve(t.Tables.size());
90 for(
const auto &t: t.Tables)
91 Tables.push_back(std::make_unique<FGTable>(*t));
98 unsigned int FindNumColumns(
const string& test_line)
102 unsigned int nCols=0;
103 while ((position = test_line.find_first_not_of(
" \t", position)) != string::npos) {
105 position = test_line.find_first_of(
" \t", position);
113 const std::string& Prefix)
114 : PropertyManager(pm)
123 if (call_type ==
"internal") {
125 }
else if (!call_type.empty()) {
127 <<
" An unknown table type attribute is listed: " << call_type
136 unsigned int dimension = 0;
145 <<
fgred <<
" This table specifies both 'internal' call type" << endl
146 <<
" and specific lookup properties via the 'independentVar' element." << endl
147 <<
" These are mutually exclusive specifications. The 'internal'" << endl
148 <<
" attribute will be ignored." <<
fgdef << endl << endl;
152 while (axisElement) {
153 string property_string = axisElement->
GetDataLine();
154 if (property_string.find(
"#") != string::npos) {
155 if (is_number(Prefix)) {
156 property_string = replace(property_string,
"#",Prefix);
161 PropertyManager, axisElement);
163 if (lookup_axis ==
string(
"row")) {
164 lookupProperty[eRow] = node;
165 dimension = std::max(dimension, 1u);
166 }
else if (lookup_axis ==
string(
"column")) {
167 lookupProperty[eColumn] = node;
168 dimension = std::max(dimension, 2u);
169 }
else if (lookup_axis ==
string(
"table")) {
170 lookupProperty[eTable] = node;
171 dimension = std::max(dimension, 3u);
172 }
else if (!lookup_axis.empty()) {
173 throw BaseException(
"Lookup table axis specification not understood: " + lookup_axis);
175 lookupProperty[eRow] = node;
176 dimension = std::max(dimension, 1u);
181 }
else if (
internal) {
191 unsigned int nColumns = FindNumColumns(tableData->
GetDataLine(0));
193 unsigned int nColumns1 = FindNumColumns(tableData->
GetDataLine(1));
194 if (nColumns1 == nColumns + 1) {
196 nColumns = nColumns1;
203 for(
unsigned int i=1; i<nLines; ++i) {
204 if (FindNumColumns(tableData->
GetDataLine(i)) != nColumns) {
206 <<
"Invalid number of columns in line "
215 if (dimension == 1 && nColumns != 2) {
217 <<
"Too many columns for a 1D table" << endl;
225 if (brkpt_string.empty()) {
229 <<
"No independentVars found, and table is not marked as internal,"
230 <<
" nor is it a 3D table." << endl;
231 throw BaseException(
"No independent variable found for table.");
236 if (brkpt_string.empty()) {
248 <<
"FGTable: <tableData> elements are missing" << endl;
249 throw BaseException(
"FGTable: <tableData> elements are missing");
252 std::cerr << tableData->
ReadFrom() <<
"<tableData> is empty." << endl;
257 if (!
internal && brkpt_string.empty()) {
260 if (!lookupProperty[eTable]) {
262 <<
"FGTable: missing lookup axis \"table\"";
263 throw BaseException(
"FGTable: missing lookup axis \"table\"");
267 if (!lookupProperty[eColumn]) {
269 <<
"FGTable: missing lookup axis \"column\"";
270 throw BaseException(
"FGTable: missing lookup axis \"column\"");
274 if (!lookupProperty[eRow]) {
276 <<
"FGTable: missing lookup axis \"row\"";
290 if (line.find_first_not_of(
"0123456789.-+eE \t\n") != string::npos) {
291 cerr <<
" In file " << tableData->
GetFileName() << endl
292 <<
" Illegal character found in line "
293 << tableData->
GetLineNumber() + i + 1 <<
": " << endl << line << endl;
305 Data.push_back(std::numeric_limits<double>::quiet_NaN());
306 Data.push_back(std::numeric_limits<double>::quiet_NaN());
314 Data.push_back(std::numeric_limits<double>::quiet_NaN());
322 Data.push_back(std::numeric_limits<double>::quiet_NaN());
326 Tables.push_back(std::make_unique<FGTable>(PropertyManager, tableData));
328 Tables.back()->lookupProperty[eRow] = lookupProperty[eRow];
329 Tables.back()->lookupProperty[eColumn] = lookupProperty[eColumn];
351 for (
unsigned int b=2; b<=Tables.size(); ++b) {
352 if (Data[b] <= Data[b-1]) {
355 <<
" FGTable: breakpoint lookup is not monotonically increasing" << endl
356 <<
" in breakpoint " << b;
357 if (nameel != 0) std::cerr <<
" of table in " << nameel->
GetAttributeValue(
"name");
358 std::cerr <<
":" <<
reset << endl
359 <<
" " << Data[b] <<
"<=" << Data[b-1] << endl;
360 throw BaseException(
"Breakpoint lookup is not monotonically increasing");
367 for (
unsigned int c=2; c<=nCols; ++c) {
368 if (Data[c] <= Data[c-1]) {
371 <<
" FGTable: column lookup is not monotonically increasing" << endl
372 <<
" in column " << c;
373 if (nameel != 0) std::cerr <<
" of table in " << nameel->
GetAttributeValue(
"name");
374 std::cerr <<
":" <<
reset << endl
375 <<
" " << Data[c] <<
"<=" << Data[c-1] << endl;
376 throw BaseException(
"FGTable: column lookup is not monotonically increasing");
383 for (
size_t r=2; r<=nRows; ++r) {
384 if (Data[r*(nCols+1)]<=Data[(r-1)*(nCols+1)]) {
387 <<
" FGTable: row lookup is not monotonically increasing" << endl
389 if (nameel != 0) std::cerr <<
" of table in " << nameel->
GetAttributeValue(
"name");
390 std::cerr <<
":" <<
reset << endl
391 <<
" " << Data[r*(nCols+1)] <<
"<=" << Data[(r-1)*(nCols+1)] << endl;
392 throw BaseException(
"FGTable: row lookup is not monotonically increasing");
400 if (Data.size() != 2*nRows+2) missingData(el, 2*nRows, Data.size()-2);
403 if (Data.size() !=
static_cast<size_t>(nRows+1)*(nCols+1))
404 missingData(el, (nRows+1)*(nCols+1)-1, Data.size()-1);
407 if (Data.size() != nRows+1) missingData(el, nRows, Data.size()-1);
416 if (debug_lvl & 1) Print();
421 void FGTable::missingData(
Element *el,
unsigned int expected_size,
size_t actual_size)
425 <<
" FGTable: Missing data";
426 if (!Name.empty()) std::cerr <<
" in table " << Name;
427 std::cerr <<
":" <<
reset << endl
428 <<
" Expecting " << expected_size <<
" elements while "
429 << actual_size <<
" elements were provided." << endl;
439 if (!Name.empty() && !
internal) {
440 string tmp = PropertyManager->mkPropertyName(Name,
false);
442 if (node && node->isTied())
443 PropertyManager->Untie(node);
451 double FGTable::GetElement(
unsigned int r,
unsigned int c)
const
453 assert(r <= nRows && c <= nCols);
455 assert(Data.size() == nRows+1);
458 assert(Data.size() == (nCols+1)*(nRows+1));
459 return Data[r*(nCols+1)+c];
470 assert(lookupProperty[eRow]);
471 return GetValue(lookupProperty[eRow]->getDoubleValue());
473 assert(lookupProperty[eRow]);
474 assert(lookupProperty[eColumn]);
475 return GetValue(lookupProperty[eRow]->getDoubleValue(),
476 lookupProperty[eColumn]->getDoubleValue());
478 assert(lookupProperty[eRow]);
479 assert(lookupProperty[eColumn]);
480 assert(lookupProperty[eTable]);
481 return GetValue(lookupProperty[eRow]->getDoubleValue(),
482 lookupProperty[eColumn]->getDoubleValue(),
483 lookupProperty[eTable]->getDoubleValue());
486 return std::numeric_limits<double>::quiet_NaN();
495 assert(Data.size() == 2*nRows+2);
500 else if (key >= Data[2*nRows])
501 return Data[2*nRows+1];
506 while (Data[2*r] < key) r++;
508 double x0 = Data[2*r-2];
509 double Span = Data[2*r] - x0;
511 double Factor = (key - x0) / Span;
512 assert(Factor >= 0.0 && Factor <= 1.0);
514 double y0 = Data[2*r-1];
515 return Factor*(Data[2*r+1] - y0) + y0;
522 if (nCols == 1)
return GetValue(rowKey);
524 assert(Type == tt2D);
525 assert(Data.size() == (nCols+1)*(nRows+1));
528 while(Data[c] < colKey && c < nCols) c++;
529 double x0 = Data[c-1];
530 double Span = Data[c] - x0;
532 double cFactor =
Constrain(0.0, (colKey - x0) / Span, 1.0);
535 double y0 = Data[(nCols+1)+c-1];
536 return cFactor*(Data[(nCols+1)+c] - y0) + y0;
540 while(Data[r*(nCols+1)] < rowKey && r < nRows) r++;
541 x0 = Data[(r-1)*(nCols+1)];
542 Span = Data[r*(nCols+1)] - x0;
544 double rFactor =
Constrain(0.0, (rowKey - x0) / Span, 1.0);
545 double col1temp = rFactor*Data[r*(nCols+1)+c-1]+(1.0-rFactor)*Data[(r-1)*(nCols+1)+c-1];
546 double col2temp = rFactor*Data[r*(nCols+1)+c]+(1.0-rFactor)*Data[(r-1)*(nCols+1)+c];
548 return cFactor*(col2temp-col1temp)+col1temp;
555 assert(Type == tt3D);
556 assert(Data.size() == nRows+1);
559 if(tableKey <= Data[1])
560 return Tables[0]->GetValue(rowKey, colKey);
561 else if (tableKey >= Data[nRows])
562 return Tables[nRows-1]->GetValue(rowKey, colKey);
567 while (Data[r] < tableKey) r++;
569 double x0 = Data[r-1];
570 double Span = Data[r] - x0;
572 double Factor = (tableKey - x0) / Span;
573 assert(Factor >= 0.0 && Factor <= 1.0);
575 double y0 = Tables[r-2]->GetValue(rowKey, colKey);
576 return Factor*(Tables[r-1]->GetValue(rowKey, colKey) - y0) + y0;
581 double FGTable::GetMinValue(
void)
const
583 assert(Type == tt1D);
584 assert(Data.size() == 2*nRows+2);
586 double minValue = HUGE_VAL;
588 for(
unsigned int i=1; i<=nRows; ++i)
589 minValue = std::min(minValue, Data[2*i+1]);
599 assert(Type != tt3D);
612 assert(Type != tt3D);
616 size_t n = Data.size();
617 if (Type == tt2D && nCols > 1 && n >= 3 && n <= nCols+1) {
618 if (Data.at(n-1) <= Data.at(n-2))
619 throw BaseException(
"FGTable: column lookup is not monotonically increasing");
623 size_t row = (n-1) / (nCols+1);
624 if (row >=2 && row*(nCols+1) == n-1) {
625 if (Data.at(row*(nCols+1)) <= Data.at((row-1)*(nCols+1)))
626 throw BaseException(
"FGTable: row lookup is not monotonically increasing");
634 void FGTable::Print(
void)
636 ios::fmtflags flags = cout.setf(ios::fixed);
641 cout <<
" 1 dimensional table with " << nRows <<
" rows." << endl;
644 cout <<
" 2 dimensional table with " << nRows <<
" rows, " << nCols <<
" columns." << endl;
647 cout <<
" 3 dimensional table with " << nRows <<
" breakpoints, "
648 << Tables.size() <<
" tables." << endl;
651 unsigned int startCol=1, startRow=1;
658 if (Type == tt2D) startRow = 0;
660 for (
unsigned int r=startRow; r<=nRows; r++) {
669 for (
unsigned int c=startCol; c<=nCols; c++) {
670 cout << Data[p++] <<
"\t";
673 Tables[r-1]->Print();
683 void FGTable::bind(Element* el,
const string& Prefix)
685 if ( !Name.empty() && !
internal) {
686 if (!Prefix.empty()) {
687 if (is_number(Prefix)) {
688 if (Name.find(
"#") != string::npos) {
689 Name = replace(Name,
"#", Prefix);
691 cerr << el->ReadFrom()
692 <<
"Malformed table name with number: " << Prefix
693 <<
" and property name: " << Name
694 <<
" but no \"#\" sign for substitution." << endl;
695 throw BaseException(
"Missing \"#\" sign for substitution");
698 Name = Prefix +
"/" + Name;
701 string tmp = PropertyManager->mkPropertyName(Name,
false);
703 if (PropertyManager->HasNode(tmp)) {
704 FGPropertyNode* _property = PropertyManager->GetNode(tmp);
705 if (_property->isTied()) {
706 cerr << el->ReadFrom()
707 <<
"Property " << tmp <<
" has already been successfully bound (late)." << endl;
708 throw BaseException(
"Failed to bind the property to an existing already tied node.");
734 void FGTable::Debug(
int from)
736 if (debug_lvl <= 0)
return;
741 if (debug_lvl & 2 ) {
742 if (from == 0) cout <<
"Instantiated: FGTable" << endl;
743 if (from == 1) cout <<
"Destroyed: FGTable" << endl;
745 if (debug_lvl & 4 ) {
747 if (debug_lvl & 8 ) {
749 if (debug_lvl & 16) {
751 if (debug_lvl & 64) {
Element * FindElement(const std::string &el="")
Searches for a specified element.
double GetAttributeValueAsNumber(const std::string &key)
Retrieves an attribute value as a double precision real number.
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
int GetLineNumber(void) const
Returns the line number at which the element has been defined.
std::string GetDataLine(unsigned int i=0)
Gets a line of data belonging to an element.
unsigned int GetNumDataLines(void)
Returns the number of lines of data stored.
const std::string & GetFileName(void) const
Returns the name of the file in which the element has been read.
unsigned int GetNumElements(void)
Returns the number of child elements for this element.
std::string ReadFrom(void) const
Return a string that contains a description of the location where the current XML element was read fr...
Element * GetParent(void)
Returns a pointer to the parent of an element.
Element * FindNextElement(const std::string &el="")
Searches for the next element as specified.
static char fgred[6]
red text
static constexpr double Constrain(double min, double value, double max)
Constrain a value between a minimum and a maximum value.
static char fgdef[6]
default text
static char reset[5]
resets text properties
static char highint[5]
highlights text
Class wrapper for property handling.
Represents a property value which can use late binding.
void operator<<(std::istream &)
Read the table in.
FGTable(const FGTable &table)
This is the very important copy constructor.
double GetValue(void) const
Get the current table value.