<commit_before>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#if defined(OS_WIN)
#include <process.h>
#include <windows.h>
#endif

#include "base/multiprocess_test.h"
#include "base/platform_thread.h"
#include "base/simple_thread.h"
#include "base/shared_memory.h"
#include "base/stats_table.h"
#include "base/stats_counters.h"
#include "base/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"

namespace base {

class StatsTableTest : public MultiProcessTest {
 public:
  void DeleteShmem(std::string name) {
    base::SharedMemory mem;
    mem.Delete(UTF8ToWide(name));
  }
};

// Open a StatsTable and verify that we can write to each of the
// locations in the table.
TEST_F(StatsTableTest, VerifySlots) {
  const std::string kTableName = "VerifySlotsStatTable";
  const int kMaxThreads = 1;
  const int kMaxCounter = 5;
  DeleteShmem(kTableName);
  StatsTable table(kTableName, kMaxThreads, kMaxCounter);

  // Register a single thread.
  std::string thread_name = "mainThread";
  int slot_id = table.RegisterThread(thread_name);
  EXPECT_NE(slot_id, 0);

  // Fill up the table with counters.
  std::string counter_base_name = "counter";
  for (int index = 0; index < kMaxCounter; index++) {
    std::string counter_name = counter_base_name;
    StringAppendF(&counter_name, "counter.ctr%d", index);
    int counter_id = table.FindCounter(counter_name);
    EXPECT_GT(counter_id, 0);
  }

  // Try to allocate an additional thread.  Verify it fails.
  slot_id = table.RegisterThread("too many threads");
  EXPECT_EQ(slot_id, 0);

  // Try to allocate an additional counter.  Verify it fails.
  int counter_id = table.FindCounter(counter_base_name);
  EXPECT_EQ(counter_id, 0);

  DeleteShmem(kTableName);
}

// CounterZero will continually be set to 0.
const std::string kCounterZero = "CounterZero";
// Counter1313 will continually be set to 1313.
const std::string kCounter1313 = "Counter1313";
// CounterIncrement will be incremented each time.
const std::string kCounterIncrement = "CounterIncrement";
// CounterDecrement will be decremented each time.
const std::string kCounterDecrement = "CounterDecrement";
// CounterMixed will be incremented by odd numbered threads and
// decremented by even threads.
const std::string kCounterMixed = "CounterMixed";
// The number of thread loops that we will do.
const int kThreadLoops = 1000;

class StatsTableThread : public base::SimpleThread {
 public:
  StatsTableThread(std::string name, int id)
      : base::SimpleThread(name), id_(id) { }
  virtual void Run();
 private:
  int id_;
};

void StatsTableThread::Run() {
  // Each thread will open the shared memory and set counters
  // concurrently in a loop.  We'll use some pauses to
  // mixup the thread scheduling.

  StatsCounter zero_counter(kCounterZero);
  StatsCounter lucky13_counter(kCounter1313);
  StatsCounter increment_counter(kCounterIncrement);
  StatsCounter decrement_counter(kCounterDecrement);
  for (int index = 0; index < kThreadLoops; index++) {
    StatsCounter mixed_counter(kCounterMixed);  // create this one in the loop
    zero_counter.Set(0);
    lucky13_counter.Set(1313);
    increment_counter.Increment();
    decrement_counter.Decrement();
    if (id_ % 2)
      mixed_counter.Decrement();
    else
      mixed_counter.Increment();
    PlatformThread::Sleep(index % 10);   // short wait
  }
}

// Create a few threads and have them poke on their counters.
// Disabled, http://crbug.com/10611.
TEST_F(StatsTableTest, DISABLED_MultipleThreads) {
  // Create a stats table.
  const std::string kTableName = "MultipleThreadStatTable";
  const int kMaxThreads = 20;
  const int kMaxCounter = 5;
  DeleteShmem(kTableName);
  StatsTable table(kTableName, kMaxThreads, kMaxCounter);
  StatsTable::set_current(&table);

  EXPECT_EQ(0, table.CountThreadsRegistered());

  // Spin up a set of threads to go bang on the various counters.
  // After we join the threads, we'll make sure the counters
  // contain the values we expected.
  StatsTableThread* threads[kMaxThreads];

  // Spawn the threads.
  for (int index = 0; index < kMaxThreads; index++) {
    threads[index] = new StatsTableThread("MultipleThreadsTest", index);
    threads[index]->Start();
  }

  // Wait for the threads to finish.
  for (int index = 0; index < kMaxThreads; index++) {
    threads[index]->Join();
    delete threads[index];
  }

  StatsCounter zero_counter(kCounterZero);
  StatsCounter lucky13_counter(kCounter1313);
  StatsCounter increment_counter(kCounterIncrement);
  StatsCounter decrement_counter(kCounterDecrement);
  StatsCounter mixed_counter(kCounterMixed);

  // Verify the various counters are correct.
  std::string name;
  name = "c:" + kCounterZero;
  EXPECT_EQ(0, table.GetCounterValue(name));
  name = "c:" + kCounter1313;
  EXPECT_EQ(1313 * kMaxThreads,
      table.GetCounterValue(name));
  name = "c:" + kCounterIncrement;
  EXPECT_EQ(kMaxThreads * kThreadLoops,
      table.GetCounterValue(name));
  name = "c:" + kCounterDecrement;
  EXPECT_EQ(-kMaxThreads * kThreadLoops,
      table.GetCounterValue(name));
  name = "c:" + kCounterMixed;
  EXPECT_EQ((kMaxThreads % 2) * kThreadLoops,
      table.GetCounterValue(name));
  EXPECT_EQ(0, table.CountThreadsRegistered());

  DeleteShmem(kTableName);
}

const std::string kMPTableName = "MultipleProcessStatTable";

MULTIPROCESS_TEST_MAIN(StatsTableMultipleProcessMain) {
  // Each process will open the shared memory and set counters
  // concurrently in a loop.  We'll use some pauses to
  // mixup the scheduling.

  StatsTable table(kMPTableName, 0, 0);
  StatsTable::set_current(&table);
  StatsCounter zero_counter(kCounterZero);
  StatsCounter lucky13_counter(kCounter1313);
  StatsCounter increment_counter(kCounterIncrement);
  StatsCounter decrement_counter(kCounterDecrement);
  for (int index = 0; index < kThreadLoops; index++) {
    zero_counter.Set(0);
    lucky13_counter.Set(1313);
    increment_counter.Increment();
    decrement_counter.Decrement();
    PlatformThread::Sleep(index % 10);   // short wait
  }
  return 0;
}

// Create a few processes and have them poke on their counters.
TEST_F(StatsTableTest, MultipleProcesses) {
  // Create a stats table.
  const int kMaxProcs = 20;
  const int kMaxCounter = 5;
  DeleteShmem(kMPTableName);
  StatsTable table(kMPTableName, kMaxProcs, kMaxCounter);
  StatsTable::set_current(&table);
  EXPECT_EQ(0, table.CountThreadsRegistered());

  // Spin up a set of processes to go bang on the various counters.
  // After we join the processes, we'll make sure the counters
  // contain the values we expected.
  ProcessHandle procs[kMaxProcs];

  // Spawn the processes.
  for (int16 index = 0; index < kMaxProcs; index++) {
    procs[index] = this->SpawnChild(L"StatsTableMultipleProcessMain");
    EXPECT_NE(base::kNullProcessHandle, procs[index]);
  }

  // Wait for the processes to finish.
  for (int index = 0; index < kMaxProcs; index++) {
    EXPECT_TRUE(WaitForSingleProcess(procs[index], 60 * 1000));
    base::CloseProcessHandle(procs[index]);
  }

  StatsCounter zero_counter(kCounterZero);
  StatsCounter lucky13_counter(kCounter1313);
  StatsCounter increment_counter(kCounterIncrement);
  StatsCounter decrement_counter(kCounterDecrement);

  // Verify the various counters are correct.
  std::string name;
  name = "c:" + kCounterZero;
  EXPECT_EQ(0, table.GetCounterValue(name));
  name = "c:" + kCounter1313;
  EXPECT_EQ(1313 * kMaxProcs,
      table.GetCounterValue(name));
  name = "c:" + kCounterIncrement;
  EXPECT_EQ(kMaxProcs * kThreadLoops,
      table.GetCounterValue(name));
  name = "c:" + kCounterDecrement;
  EXPECT_EQ(-kMaxProcs * kThreadLoops,
      table.GetCounterValue(name));
  EXPECT_EQ(0, table.CountThreadsRegistered());

  DeleteShmem(kMPTableName);
}

class MockStatsCounter : public StatsCounter {
 public:
  explicit MockStatsCounter(const std::string& name)
      : StatsCounter(name) {}
  int* Pointer() { return GetPtr(); }
};

// Test some basic StatsCounter operations
TEST_F(StatsTableTest, StatsCounter) {
  // Create a stats table.
  const std::string kTableName = "StatTable";
  const int kMaxThreads = 20;
  const int kMaxCounter = 5;
  DeleteShmem(kTableName);
  StatsTable table(kTableName, kMaxThreads, kMaxCounter);
  StatsTable::set_current(&table);

  MockStatsCounter foo("foo");

  // Test initial state.
  EXPECT_TRUE(foo.Enabled());
  ASSERT_NE(foo.Pointer(), static_cast<int*>(0));
  EXPECT_EQ(0, *(foo.Pointer()));
  EXPECT_EQ(0, table.GetCounterValue("c:foo"));

  // Test Increment.
  while (*(foo.Pointer()) < 123) foo.Increment();
  EXPECT_EQ(123, table.GetCounterValue("c:foo"));
  foo.Add(0);
  EXPECT_EQ(123, table.GetCounterValue("c:foo"));
  foo.Add(-1);
  EXPECT_EQ(122, table.GetCounterValue("c:foo"));

  // Test Set.
  foo.Set(0);
  EXPECT_EQ(0, table.GetCounterValue("c:foo"));
  foo.Set(100);
  EXPECT_EQ(100, table.GetCounterValue("c:foo"));
  foo.Set(-1);
  EXPECT_EQ(-1, table.GetCounterValue("c:foo"));
  foo.Set(0);
  EXPECT_EQ(0, table.GetCounterValue("c:foo"));

  // Test Decrement.
  foo.Subtract(1);
  EXPECT_EQ(-1, table.GetCounterValue("c:foo"));
  foo.Subtract(0);
  EXPECT_EQ(-1, table.GetCounterValue("c:foo"));
  foo.Subtract(-1);
  EXPECT_EQ(0, table.GetCounterValue("c:foo"));

  DeleteShmem(kTableName);
}

class MockStatsCounterTimer : public StatsCounterTimer {
 public:
  explicit MockStatsCounterTimer(const std::string& name)
      : StatsCounterTimer(name) {}

  TimeTicks start_time() { return start_time_; }
  TimeTicks stop_time() { return stop_time_; }
};

// Test some basic StatsCounterTimer operations
TEST_F(StatsTableTest, StatsCounterTimer) {
  // Create a stats table.
  const std::string kTableName = "StatTable";
  const int kMaxThreads = 20;
  const int kMaxCounter = 5;
  StatsTable table(kTableName, kMaxThreads, kMaxCounter);
  StatsTable::set_current(&table);

  MockStatsCounterTimer bar("bar");

  // Test initial state.
  EXPECT_FALSE(bar.Running());
  EXPECT_TRUE(bar.start_time().is_null());
  EXPECT_TRUE(bar.stop_time().is_null());

  // Do some timing.
  bar.Start();
  PlatformThread::Sleep(500);
  bar.Stop();
  EXPECT_LE(500, table.GetCounterValue("t:bar"));

  // Verify that timing again is additive.
  bar.Start();
  PlatformThread::Sleep(500);
  bar.Stop();
  EXPECT_LE(1000, table.GetCounterValue("t:bar"));
}

// Test some basic StatsRate operations
TEST_F(StatsTableTest, StatsRate) {
  // Create a stats table.
  const std::string kTableName = "StatTable";
  const int kMaxThreads = 20;
  const int kMaxCounter = 5;
  StatsTable table(kTableName, kMaxThreads, kMaxCounter);
  StatsTable::set_current(&table);

  StatsRate baz("baz");

  // Test initial state.
  EXPECT_FALSE(baz.Running());
  EXPECT_EQ(0, table.GetCounterValue("c:baz"));
  EXPECT_EQ(0, table.GetCounterValue("t:baz"));

  // Do some timing.
  baz.Start();
  PlatformThread::Sleep(500);
  baz.Stop();
  EXPECT_EQ(1, table.GetCounterValue("c:baz"));
  EXPECT_LE(500, table.GetCounterValue("t:baz"));

  // Verify that timing again is additive.
  baz.Start();
  PlatformThread::Sleep(500);
  baz.Stop();
  EXPECT_EQ(2, table.GetCounterValue("c:baz"));
  EXPECT_LE(1000, table.GetCounterValue("t:baz"));
}

// Test some basic StatsScope operations
TEST_F(StatsTableTest, StatsScope) {
  // Create a stats table.
  const std::string kTableName = "StatTable";
  const int kMaxThreads = 20;
  const int kMaxCounter = 5;
  DeleteShmem(kTableName);
  StatsTable table(kTableName, kMaxThreads, kMaxCounter);
  StatsTable::set_current(&table);

  StatsCounterTimer foo("foo");
  StatsRate bar("bar");

  // Test initial state.
  EXPECT_EQ(0, table.GetCounterValue("t:foo"));
  EXPECT_EQ(0, table.GetCounterValue("t:bar"));
  EXPECT_EQ(0, table.GetCounterValue("c:bar"));

  // Try a scope.
  {
    StatsScope<StatsCounterTimer> timer(foo);
    StatsScope<StatsRate> timer2(bar);
    PlatformThread::Sleep(500);
  }
  EXPECT_LE(500, table.GetCounterValue("t:foo"));
  EXPECT_LE(500, table.GetCounterValue("t:bar"));
  EXPECT_EQ(1, table.GetCounterValue("c:bar"));

  // Try a second scope.
  {
    StatsScope<StatsCounterTimer> timer(foo);
    StatsScope<StatsRate> timer2(bar);
    PlatformThread::Sleep(500);
  }
  EXPECT_LE(1000, table.GetCounterValue("t:foo"));
  EXPECT_LE(1000, table.GetCounterValue("t:bar"));
  EXPECT_EQ(2, table.GetCounterValue("c:bar"));

  DeleteShmem(kTableName);
}

}  // namespace base
<commit_msg>TTF: Re-enable StatsTableTest.MultipleThreads by marking it flaky.<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#if defined(OS_WIN)
#include <process.h>
#include <windows.h>
#endif

#include "base/multiprocess_test.h"
#include "base/platform_thread.h"
#include "base/simple_thread.h"
#include "base/shared_memory.h"
#include "base/stats_table.h"
#include "base/stats_counters.h"
#include "base/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"

namespace base {

class StatsTableTest : public MultiProcessTest {
 public:
  void DeleteShmem(std::string name) {
    base::SharedMemory mem;
    mem.Delete(UTF8ToWide(name));
  }
};

// Open a StatsTable and verify that we can write to each of the
// locations in the table.
TEST_F(StatsTableTest, VerifySlots) {
  const std::string kTableName = "VerifySlotsStatTable";
  const int kMaxThreads = 1;
  const int kMaxCounter = 5;
  DeleteShmem(kTableName);
  StatsTable table(kTableName, kMaxThreads, kMaxCounter);

  // Register a single thread.
  std::string thread_name = "mainThread";
  int slot_id = table.RegisterThread(thread_name);
  EXPECT_NE(slot_id, 0);

  // Fill up the table with counters.
  std::string counter_base_name = "counter";
  for (int index = 0; index < kMaxCounter; index++) {
    std::string counter_name = counter_base_name;
    StringAppendF(&counter_name, "counter.ctr%d", index);
    int counter_id = table.FindCounter(counter_name);
    EXPECT_GT(counter_id, 0);
  }

  // Try to allocate an additional thread.  Verify it fails.
  slot_id = table.RegisterThread("too many threads");
  EXPECT_EQ(slot_id, 0);

  // Try to allocate an additional counter.  Verify it fails.
  int counter_id = table.FindCounter(counter_base_name);
  EXPECT_EQ(counter_id, 0);

  DeleteShmem(kTableName);
}

// CounterZero will continually be set to 0.
const std::string kCounterZero = "CounterZero";
// Counter1313 will continually be set to 1313.
const std::string kCounter1313 = "Counter1313";
// CounterIncrement will be incremented each time.
const std::string kCounterIncrement = "CounterIncrement";
// CounterDecrement will be decremented each time.
const std::string kCounterDecrement = "CounterDecrement";
// CounterMixed will be incremented by odd numbered threads and
// decremented by even threads.
const std::string kCounterMixed = "CounterMixed";
// The number of thread loops that we will do.
const int kThreadLoops = 1000;

class StatsTableThread : public base::SimpleThread {
 public:
  StatsTableThread(std::string name, int id)
      : base::SimpleThread(name), id_(id) { }
  virtual void Run();
 private:
  int id_;
};

void StatsTableThread::Run() {
  // Each thread will open the shared memory and set counters
  // concurrently in a loop.  We'll use some pauses to
  // mixup the thread scheduling.

  StatsCounter zero_counter(kCounterZero);
  StatsCounter lucky13_counter(kCounter1313);
  StatsCounter increment_counter(kCounterIncrement);
  StatsCounter decrement_counter(kCounterDecrement);
  for (int index = 0; index < kThreadLoops; index++) {
    StatsCounter mixed_counter(kCounterMixed);  // create this one in the loop
    zero_counter.Set(0);
    lucky13_counter.Set(1313);
    increment_counter.Increment();
    decrement_counter.Decrement();
    if (id_ % 2)
      mixed_counter.Decrement();
    else
      mixed_counter.Increment();
    PlatformThread::Sleep(index % 10);   // short wait
  }
}

// Create a few threads and have them poke on their counters.
// Flaky, http://crbug.com/10611.
TEST_F(StatsTableTest, FLAKY_MultipleThreads) {
  // Create a stats table.
  const std::string kTableName = "MultipleThreadStatTable";
  const int kMaxThreads = 20;
  const int kMaxCounter = 5;
  DeleteShmem(kTableName);
  StatsTable table(kTableName, kMaxThreads, kMaxCounter);
  StatsTable::set_current(&table);

  EXPECT_EQ(0, table.CountThreadsRegistered());

  // Spin up a set of threads to go bang on the various counters.
  // After we join the threads, we'll make sure the counters
  // contain the values we expected.
  StatsTableThread* threads[kMaxThreads];

  // Spawn the threads.
  for (int index = 0; index < kMaxThreads; index++) {
    threads[index] = new StatsTableThread("MultipleThreadsTest", index);
    threads[index]->Start();
  }

  // Wait for the threads to finish.
  for (int index = 0; index < kMaxThreads; index++) {
    threads[index]->Join();
    delete threads[index];
  }

  StatsCounter zero_counter(kCounterZero);
  StatsCounter lucky13_counter(kCounter1313);
  StatsCounter increment_counter(kCounterIncrement);
  StatsCounter decrement_counter(kCounterDecrement);
  StatsCounter mixed_counter(kCounterMixed);

  // Verify the various counters are correct.
  std::string name;
  name = "c:" + kCounterZero;
  EXPECT_EQ(0, table.GetCounterValue(name));
  name = "c:" + kCounter1313;
  EXPECT_EQ(1313 * kMaxThreads,
      table.GetCounterValue(name));
  name = "c:" + kCounterIncrement;
  EXPECT_EQ(kMaxThreads * kThreadLoops,
      table.GetCounterValue(name));
  name = "c:" + kCounterDecrement;
  EXPECT_EQ(-kMaxThreads * kThreadLoops,
      table.GetCounterValue(name));
  name = "c:" + kCounterMixed;
  EXPECT_EQ((kMaxThreads % 2) * kThreadLoops,
      table.GetCounterValue(name));
  EXPECT_EQ(0, table.CountThreadsRegistered());

  DeleteShmem(kTableName);
}

const std::string kMPTableName = "MultipleProcessStatTable";

MULTIPROCESS_TEST_MAIN(StatsTableMultipleProcessMain) {
  // Each process will open the shared memory and set counters
  // concurrently in a loop.  We'll use some pauses to
  // mixup the scheduling.

  StatsTable table(kMPTableName, 0, 0);
  StatsTable::set_current(&table);
  StatsCounter zero_counter(kCounterZero);
  StatsCounter lucky13_counter(kCounter1313);
  StatsCounter increment_counter(kCounterIncrement);
  StatsCounter decrement_counter(kCounterDecrement);
  for (int index = 0; index < kThreadLoops; index++) {
    zero_counter.Set(0);
    lucky13_counter.Set(1313);
    increment_counter.Increment();
    decrement_counter.Decrement();
    PlatformThread::Sleep(index % 10);   // short wait
  }
  return 0;
}

// Create a few processes and have them poke on their counters.
TEST_F(StatsTableTest, MultipleProcesses) {
  // Create a stats table.
  const int kMaxProcs = 20;
  const int kMaxCounter = 5;
  DeleteShmem(kMPTableName);
  StatsTable table(kMPTableName, kMaxProcs, kMaxCounter);
  StatsTable::set_current(&table);
  EXPECT_EQ(0, table.CountThreadsRegistered());

  // Spin up a set of processes to go bang on the various counters.
  // After we join the processes, we'll make sure the counters
  // contain the values we expected.
  ProcessHandle procs[kMaxProcs];

  // Spawn the processes.
  for (int16 index = 0; index < kMaxProcs; index++) {
    procs[index] = this->SpawnChild(L"StatsTableMultipleProcessMain");
    EXPECT_NE(base::kNullProcessHandle, procs[index]);
  }

  // Wait for the processes to finish.
  for (int index = 0; index < kMaxProcs; index++) {
    EXPECT_TRUE(WaitForSingleProcess(procs[index], 60 * 1000));
    base::CloseProcessHandle(procs[index]);
  }

  StatsCounter zero_counter(kCounterZero);
  StatsCounter lucky13_counter(kCounter1313);
  StatsCounter increment_counter(kCounterIncrement);
  StatsCounter decrement_counter(kCounterDecrement);

  // Verify the various counters are correct.
  std::string name;
  name = "c:" + kCounterZero;
  EXPECT_EQ(0, table.GetCounterValue(name));
  name = "c:" + kCounter1313;
  EXPECT_EQ(1313 * kMaxProcs,
      table.GetCounterValue(name));
  name = "c:" + kCounterIncrement;
  EXPECT_EQ(kMaxProcs * kThreadLoops,
      table.GetCounterValue(name));
  name = "c:" + kCounterDecrement;
  EXPECT_EQ(-kMaxProcs * kThreadLoops,
      table.GetCounterValue(name));
  EXPECT_EQ(0, table.CountThreadsRegistered());

  DeleteShmem(kMPTableName);
}

class MockStatsCounter : public StatsCounter {
 public:
  explicit MockStatsCounter(const std::string& name)
      : StatsCounter(name) {}
  int* Pointer() { return GetPtr(); }
};

// Test some basic StatsCounter operations
TEST_F(StatsTableTest, StatsCounter) {
  // Create a stats table.
  const std::string kTableName = "StatTable";
  const int kMaxThreads = 20;
  const int kMaxCounter = 5;
  DeleteShmem(kTableName);
  StatsTable table(kTableName, kMaxThreads, kMaxCounter);
  StatsTable::set_current(&table);

  MockStatsCounter foo("foo");

  // Test initial state.
  EXPECT_TRUE(foo.Enabled());
  ASSERT_NE(foo.Pointer(), static_cast<int*>(0));
  EXPECT_EQ(0, *(foo.Pointer()));
  EXPECT_EQ(0, table.GetCounterValue("c:foo"));

  // Test Increment.
  while (*(foo.Pointer()) < 123) foo.Increment();
  EXPECT_EQ(123, table.GetCounterValue("c:foo"));
  foo.Add(0);
  EXPECT_EQ(123, table.GetCounterValue("c:foo"));
  foo.Add(-1);
  EXPECT_EQ(122, table.GetCounterValue("c:foo"));

  // Test Set.
  foo.Set(0);
  EXPECT_EQ(0, table.GetCounterValue("c:foo"));
  foo.Set(100);
  EXPECT_EQ(100, table.GetCounterValue("c:foo"));
  foo.Set(-1);
  EXPECT_EQ(-1, table.GetCounterValue("c:foo"));
  foo.Set(0);
  EXPECT_EQ(0, table.GetCounterValue("c:foo"));

  // Test Decrement.
  foo.Subtract(1);
  EXPECT_EQ(-1, table.GetCounterValue("c:foo"));
  foo.Subtract(0);
  EXPECT_EQ(-1, table.GetCounterValue("c:foo"));
  foo.Subtract(-1);
  EXPECT_EQ(0, table.GetCounterValue("c:foo"));

  DeleteShmem(kTableName);
}

class MockStatsCounterTimer : public StatsCounterTimer {
 public:
  explicit MockStatsCounterTimer(const std::string& name)
      : StatsCounterTimer(name) {}

  TimeTicks start_time() { return start_time_; }
  TimeTicks stop_time() { return stop_time_; }
};

// Test some basic StatsCounterTimer operations
TEST_F(StatsTableTest, StatsCounterTimer) {
  // Create a stats table.
  const std::string kTableName = "StatTable";
  const int kMaxThreads = 20;
  const int kMaxCounter = 5;
  StatsTable table(kTableName, kMaxThreads, kMaxCounter);
  StatsTable::set_current(&table);

  MockStatsCounterTimer bar("bar");

  // Test initial state.
  EXPECT_FALSE(bar.Running());
  EXPECT_TRUE(bar.start_time().is_null());
  EXPECT_TRUE(bar.stop_time().is_null());

  // Do some timing.
  bar.Start();
  PlatformThread::Sleep(500);
  bar.Stop();
  EXPECT_LE(500, table.GetCounterValue("t:bar"));

  // Verify that timing again is additive.
  bar.Start();
  PlatformThread::Sleep(500);
  bar.Stop();
  EXPECT_LE(1000, table.GetCounterValue("t:bar"));
}

// Test some basic StatsRate operations
TEST_F(StatsTableTest, StatsRate) {
  // Create a stats table.
  const std::string kTableName = "StatTable";
  const int kMaxThreads = 20;
  const int kMaxCounter = 5;
  StatsTable table(kTableName, kMaxThreads, kMaxCounter);
  StatsTable::set_current(&table);

  StatsRate baz("baz");

  // Test initial state.
  EXPECT_FALSE(baz.Running());
  EXPECT_EQ(0, table.GetCounterValue("c:baz"));
  EXPECT_EQ(0, table.GetCounterValue("t:baz"));

  // Do some timing.
  baz.Start();
  PlatformThread::Sleep(500);
  baz.Stop();
  EXPECT_EQ(1, table.GetCounterValue("c:baz"));
  EXPECT_LE(500, table.GetCounterValue("t:baz"));

  // Verify that timing again is additive.
  baz.Start();
  PlatformThread::Sleep(500);
  baz.Stop();
  EXPECT_EQ(2, table.GetCounterValue("c:baz"));
  EXPECT_LE(1000, table.GetCounterValue("t:baz"));
}

// Test some basic StatsScope operations
TEST_F(StatsTableTest, StatsScope) {
  // Create a stats table.
  const std::string kTableName = "StatTable";
  const int kMaxThreads = 20;
  const int kMaxCounter = 5;
  DeleteShmem(kTableName);
  StatsTable table(kTableName, kMaxThreads, kMaxCounter);
  StatsTable::set_current(&table);

  StatsCounterTimer foo("foo");
  StatsRate bar("bar");

  // Test initial state.
  EXPECT_EQ(0, table.GetCounterValue("t:foo"));
  EXPECT_EQ(0, table.GetCounterValue("t:bar"));
  EXPECT_EQ(0, table.GetCounterValue("c:bar"));

  // Try a scope.
  {
    StatsScope<StatsCounterTimer> timer(foo);
    StatsScope<StatsRate> timer2(bar);
    PlatformThread::Sleep(500);
  }
  EXPECT_LE(500, table.GetCounterValue("t:foo"));
  EXPECT_LE(500, table.GetCounterValue("t:bar"));
  EXPECT_EQ(1, table.GetCounterValue("c:bar"));

  // Try a second scope.
  {
    StatsScope<StatsCounterTimer> timer(foo);
    StatsScope<StatsRate> timer2(bar);
    PlatformThread::Sleep(500);
  }
  EXPECT_LE(1000, table.GetCounterValue("t:foo"));
  EXPECT_LE(1000, table.GetCounterValue("t:bar"));
  EXPECT_EQ(2, table.GetCounterValue("c:bar"));

  DeleteShmem(kTableName);
}

}  // namespace base
<|endoftext|>


<commit_before>/* -------------------------------------------------------------------------- *
 *                      OpenSim:  ControllerExample.cpp                       *
 * -------------------------------------------------------------------------- *
 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
 * See http://opensim.stanford.edu and the NOTICE file for more information.  *
 * OpenSim is developed at Stanford University and supported by the US        *
 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
 * through the Warrior Web program.                                           *
 *                                                                            *
 * Copyright (c) 2005-2012 Stanford University and the Authors                *
 * Author(s): Chand T. John, Ajay Seth                                        *
 *                                                                            *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
 * not use this file except in compliance with the License. You may obtain a  *
 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
 *                                                                            *
 * Unless required by applicable law or agreed to in writing, software        *
 * distributed under the License is distributed on an "AS IS" BASIS,          *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied    *
 * See the License for the specific language governing permissions and        *
 * limitations under the License.                                             *
 * -------------------------------------------------------------------------- */

/*
 *  Below is an extension example of an OpenSim application that provides its own
 *  main() routine.  It applies a controller to the forward simulation of a tug-of-war
 *  between two muscles pulling on a block.
 */

// Author:  Chand T. John and Ajay Seth

//==============================================================================
//==============================================================================

// Include OpenSim and functions
#include <OpenSim/OpenSim.h>

// This allows us to use OpenSim functions, classes, etc., without having to
// prefix the names of those things with "OpenSim::".
using namespace OpenSim;

// This allows us to use SimTK functions, classes, etc., without having to
// prefix the names of those things with "SimTK::".
using namespace SimTK;

//______________________________________________________________________________
/**
 * The controller will try to make the model follow this position
 * in the z direction.
 */
double desiredModelZPosition( double t ) {
    // z(t) = 0.15 sin( pi * t )
    return 0.15 * sin( Pi * t );
}

/**
 * The controller will try to make the model follow this velocity
 * in the z direction.
 */
double desiredModelZVelocity( double t ) {
    // z(t) = 0.15 sin( pi * t )
    return 0.15 * Pi * cos( Pi * t );
}
//______________________________________________________________________________
/**
 * The controller will try to make the model follow this acceleration
 * in the z direction.
 */
double desiredModelZAcceleration( double t ) {
    // z''(t) = -(0.15*pi^2) sin( pi * t )
    return -0.15 * Pi * Pi * sin( Pi * t );
}

//______________________________________________________________________________
/**
 * This controller will try to track a desired trajectory of the block in
 * the tug-of-war model.
 */
class TugOfWarController : public Controller {
OpenSim_DECLARE_CONCRETE_OBJECT(TugOfWarController, Controller);

// This section contains methods that can be called in this controller class.
public:
    /**
     * Constructor
     *
     * @param aModel Model to be controlled
     * @param aKp Position gain by which the position error will be multiplied
     */
    TugOfWarController(double aKp, double aKv) : Controller(), kp( aKp ), kv( aKv )
    {
    }

    /**
     * This function is called at every time step for every actuator.
     *
     * @param s Current state of the system
     * @param controls Controls being calculated
     */
    void computeControls(const SimTK::State& s, SimTK::Vector &controls) const
    {
        // Get the current time in the simulation.
        double t = s.getTime();

        // Read the mass of the block.
        double blockMass = getModel().getBodySet().get( "block" ).getMass();

        // Get pointers to each of the muscles in the model.
        Muscle* leftMuscle = dynamic_cast<Muscle*>  ( &getActuatorSet().get(0) );
        Muscle* rightMuscle = dynamic_cast<Muscle*> ( &getActuatorSet().get(1) );

        // Compute the desired position of the block in the tug-of-war
        // model.
        double zdes  = desiredModelZPosition(t);

        // Compute the desired velocity of the block in the tug-of-war
        // model.
        double zdesv = desiredModelZVelocity(t);

        // Compute the desired acceleration of the block in the tug-
        // of-war model.
        double zdesa = desiredModelZAcceleration(t);

        // Get the z translation coordinate in the model.
        const Coordinate& zCoord = _model->getCoordinateSet().
            get( "blockToGround_zTranslation" );

        // Get the current position of the block in the tug-of-war
        // model.
        double z  = zCoord.getValue(s);

        // Get the current velocity of the block in the tug-of-war
        // model.
        double zv = zCoord.getSpeedValue(s);

        // Compute the correction to the desired acceleration arising
        // from the deviation of the block's current position from its
        // desired position (this deviation is the "position error").
        double pErrTerm = kp * ( zdes  - z  );

        // Compute the total desired velocity based on the initial
        // desired velocity plus the position error term we
        // computed above.
        double vErrTerm = kv * ( zdesv - zv );

        // Compute the total desired acceleration based on the initial
        // desired acceleration plus the position error term we
        // computed above.
        double desAcc = zdesa + vErrTerm + pErrTerm;

        // Compute the desired force on the block as the mass of the
        // block times the total desired acceleration of the block.
        double desFrc = desAcc * blockMass;

        // Get the maximum isometric force for the left muscle.
        double FoptL = leftMuscle->getMaxIsometricForce();

        // Get the maximum isometric force for the right muscle.
        double FoptR = rightMuscle->getMaxIsometricForce();

        // If desired force is in direction of one muscle's pull
        // direction, then set that muscle's control based on desired
        // force.  Otherwise, set the muscle's control to zero.
        double leftControl = 0.0, rightControl = 0.0;
        if( desFrc < 0 ) {
            leftControl = abs( desFrc ) / FoptL;
            rightControl = 0.0;
        }
        else if( desFrc > 0 ) {
            leftControl = 0.0;
            rightControl = abs( desFrc ) / FoptR;
        }
        // Don't allow any control value to be greater than one.
        if( leftControl > 1.0 ) leftControl = 1.0;
        if( rightControl > 1.0 ) rightControl = 1.0;

        // Thelen muscle has only one control
        Vector muscleControl(1, leftControl);
        // Add in the controls computed for this muscle to the set of all model controls
        leftMuscle->addInControls(muscleControl, controls);
        // Specify control for other actuator (muscle) controlled by this controller
        muscleControl[0] = rightControl;
        rightMuscle->addInControls(muscleControl, controls);
    }

// This section contains the member variables of this controller class.
private:

    /** Position gain for this controller */
    double kp;
    /** Velocity gain for this controller */
    double kv;

};

//______________________________________________________________________________
/**
 * Run a forward dynamics simulation with a controller attached to a model.
 * The model consists of a block attached by two muscles to two walls.  The
 * block can make contact with the ground.
 */
int main()
{
    try {

                // Need to load this DLL so muscle types are recognized.
        LoadOpenSimLibrary( "osimActuators" );

        // Create an OpenSim model from the model file provided.
        Model osimModel( "tugOfWar_model_ThelenOnly.osim" );

        // Define the initial and final simulation times.
        double initialTime = 0.0;
        double finalTime = 1.0;

        // Set gain for the controller.
        double kp = 1600.0; // position gain
        double kv = 80.0;   // velocity gain

        // Print the control gains and block mass.
        std::cout << std::endl;
        std::cout << "kp = " << kp << std::endl;
        std::cout << "kv = " << kv << std::endl;

        // Create the controller.
        TugOfWarController *controller = new TugOfWarController(kp, kv);

        // Give the controller the Model's actuators so it knows
        // to control those actuators.
        controller->setActuators( osimModel.updActuators() );

        // Add the controller to the Model.
        osimModel.addController( controller );

        // Initialize the system and get the state representing the
        // system.
        SimTK::State& si = osimModel.initSystem();

        // Define non-zero (defaults are 0) states for the free joint.
        CoordinateSet& modelCoordinateSet =
            osimModel.updCoordinateSet();
        // Get the z translation coordinate.
        Coordinate& zCoord = modelCoordinateSet.
            get( "blockToGround_zTranslation" );
        // Set z translation speed value.
        zCoord.setSpeedValue( si, 0.15 * Pi );

        // Define the initial muscle states.
        const Set<Muscle>& muscleSet = osimModel.getMuscles();
        ActivationFiberLengthMuscle* muscle1 = dynamic_cast<ActivationFiberLengthMuscle*>( &muscleSet.get(0) );
        ActivationFiberLengthMuscle* muscle2 = dynamic_cast<ActivationFiberLengthMuscle*>( &muscleSet.get(1) );
        if((muscle1 == NULL) || (muscle2 == NULL)){
            throw OpenSim::Exception("ControllerExample: muscle1 or muscle2 is not an ActivationFiberLengthMuscle and example cannot proceed.");
        }
        muscle1->setActivation(si, 0.01 ); // muscle1 activation
        muscle1->setFiberLength(si, 0.2 ); // muscle1 fiber length
        muscle2->setActivation(si, 0.01 ); // muscle2 activation
        muscle2->setFiberLength(si, 0.2 ); // muscle2 fiber length

        // Create the integrator and manager for the simulation.
        SimTK::RungeKuttaMersonIntegrator
            integrator( osimModel.getMultibodySystem() );
        integrator.setAccuracy( 1.0e-4 );

        Manager manager( osimModel, integrator );

        // Examine the model.
        osimModel.printDetailedInfo( si, std::cout );

        // Print out the initial position and velocity states.
        for( int i = 0; i < modelCoordinateSet.getSize(); i++ ) {
            std::cout << "Initial " << modelCoordinateSet[i].getName()
                << " = " << modelCoordinateSet[i].getValue( si )
                << ", and speed = "
                << modelCoordinateSet[i].getSpeedValue( si ) << std::endl;
        }

        // Integrate from initial time to final time.
        manager.setInitialTime( initialTime );
        manager.setFinalTime( finalTime );
        std::cout << "\n\nIntegrating from " << initialTime
            << " to " << finalTime << std::endl;
        manager.integrate( si );

        // Save the simulation results.
        osimModel.printControlStorage( "tugOfWar_controls.sto" );
        manager.getStateStorage().print( "tugOfWar_states.sto" );
    }
    catch (const std::exception &ex) {

        // In case of an exception, print it out to the screen.
        std::cout << ex.what() << std::endl;

        // Return 1 instead of 0 to indicate that something
        // undesirable happened.
        return 1;
    }

    // If this program executed up to this line, return 0 to
    // indicate that the intended lines of code were executed.
    return 0;
}
<commit_msg>Replace Storage calls with TimeSeriesTable calls.<commit_after>/* -------------------------------------------------------------------------- *
 *                      OpenSim:  ControllerExample.cpp                       *
 * -------------------------------------------------------------------------- *
 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
 * See http://opensim.stanford.edu and the NOTICE file for more information.  *
 * OpenSim is developed at Stanford University and supported by the US        *
 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
 * through the Warrior Web program.                                           *
 *                                                                            *
 * Copyright (c) 2005-2012 Stanford University and the Authors                *
 * Author(s): Chand T. John, Ajay Seth                                        *
 *                                                                            *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
 * not use this file except in compliance with the License. You may obtain a  *
 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
 *                                                                            *
 * Unless required by applicable law or agreed to in writing, software        *
 * distributed under the License is distributed on an "AS IS" BASIS,          *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied    *
 * See the License for the specific language governing permissions and        *
 * limitations under the License.                                             *
 * -------------------------------------------------------------------------- */

/*
 *  Below is an extension example of an OpenSim application that provides its own
 *  main() routine.  It applies a controller to the forward simulation of a tug-of-war
 *  between two muscles pulling on a block.
 */

// Author:  Chand T. John and Ajay Seth

//==============================================================================
//==============================================================================

// Include OpenSim and functions
#include <OpenSim/OpenSim.h>
#include "OpenSim/Common/MOTFileAdapter.h"

// This allows us to use OpenSim functions, classes, etc., without having to
// prefix the names of those things with "OpenSim::".
using namespace OpenSim;

// This allows us to use SimTK functions, classes, etc., without having to
// prefix the names of those things with "SimTK::".
using namespace SimTK;

//______________________________________________________________________________
/**
 * The controller will try to make the model follow this position
 * in the z direction.
 */
double desiredModelZPosition( double t ) {
    // z(t) = 0.15 sin( pi * t )
    return 0.15 * sin( Pi * t );
}

/**
 * The controller will try to make the model follow this velocity
 * in the z direction.
 */
double desiredModelZVelocity( double t ) {
    // z(t) = 0.15 sin( pi * t )
    return 0.15 * Pi * cos( Pi * t );
}
//______________________________________________________________________________
/**
 * The controller will try to make the model follow this acceleration
 * in the z direction.
 */
double desiredModelZAcceleration( double t ) {
    // z''(t) = -(0.15*pi^2) sin( pi * t )
    return -0.15 * Pi * Pi * sin( Pi * t );
}

//______________________________________________________________________________
/**
 * This controller will try to track a desired trajectory of the block in
 * the tug-of-war model.
 */
class TugOfWarController : public Controller {
OpenSim_DECLARE_CONCRETE_OBJECT(TugOfWarController, Controller);

// This section contains methods that can be called in this controller class.
public:
    /**
     * Constructor
     *
     * @param aModel Model to be controlled
     * @param aKp Position gain by which the position error will be multiplied
     */
    TugOfWarController(double aKp, double aKv) : Controller(), kp( aKp ), kv( aKv )
    {
    }

    /**
     * This function is called at every time step for every actuator.
     *
     * @param s Current state of the system
     * @param controls Controls being calculated
     */
    void computeControls(const SimTK::State& s, SimTK::Vector &controls) const
    {
        // Get the current time in the simulation.
        double t = s.getTime();

        // Read the mass of the block.
        double blockMass = getModel().getBodySet().get( "block" ).getMass();

        // Get pointers to each of the muscles in the model.
        Muscle* leftMuscle = dynamic_cast<Muscle*>  ( &getActuatorSet().get(0) );
        Muscle* rightMuscle = dynamic_cast<Muscle*> ( &getActuatorSet().get(1) );

        // Compute the desired position of the block in the tug-of-war
        // model.
        double zdes  = desiredModelZPosition(t);

        // Compute the desired velocity of the block in the tug-of-war
        // model.
        double zdesv = desiredModelZVelocity(t);

        // Compute the desired acceleration of the block in the tug-
        // of-war model.
        double zdesa = desiredModelZAcceleration(t);

        // Get the z translation coordinate in the model.
        const Coordinate& zCoord = _model->getCoordinateSet().
            get( "blockToGround_zTranslation" );

        // Get the current position of the block in the tug-of-war
        // model.
        double z  = zCoord.getValue(s);

        // Get the current velocity of the block in the tug-of-war
        // model.
        double zv = zCoord.getSpeedValue(s);

        // Compute the correction to the desired acceleration arising
        // from the deviation of the block's current position from its
        // desired position (this deviation is the "position error").
        double pErrTerm = kp * ( zdes  - z  );

        // Compute the total desired velocity based on the initial
        // desired velocity plus the position error term we
        // computed above.
        double vErrTerm = kv * ( zdesv - zv );

        // Compute the total desired acceleration based on the initial
        // desired acceleration plus the position error term we
        // computed above.
        double desAcc = zdesa + vErrTerm + pErrTerm;

        // Compute the desired force on the block as the mass of the
        // block times the total desired acceleration of the block.
        double desFrc = desAcc * blockMass;

        // Get the maximum isometric force for the left muscle.
        double FoptL = leftMuscle->getMaxIsometricForce();

        // Get the maximum isometric force for the right muscle.
        double FoptR = rightMuscle->getMaxIsometricForce();

        // If desired force is in direction of one muscle's pull
        // direction, then set that muscle's control based on desired
        // force.  Otherwise, set the muscle's control to zero.
        double leftControl = 0.0, rightControl = 0.0;
        if( desFrc < 0 ) {
            leftControl = abs( desFrc ) / FoptL;
            rightControl = 0.0;
        }
        else if( desFrc > 0 ) {
            leftControl = 0.0;
            rightControl = abs( desFrc ) / FoptR;
        }
        // Don't allow any control value to be greater than one.
        if( leftControl > 1.0 ) leftControl = 1.0;
        if( rightControl > 1.0 ) rightControl = 1.0;

        // Thelen muscle has only one control
        Vector muscleControl(1, leftControl);
        // Add in the controls computed for this muscle to the set of all model controls
        leftMuscle->addInControls(muscleControl, controls);
        // Specify control for other actuator (muscle) controlled by this controller
        muscleControl[0] = rightControl;
        rightMuscle->addInControls(muscleControl, controls);
    }

// This section contains the member variables of this controller class.
private:

    /** Position gain for this controller */
    double kp;
    /** Velocity gain for this controller */
    double kv;

};

//______________________________________________________________________________
/**
 * Run a forward dynamics simulation with a controller attached to a model.
 * The model consists of a block attached by two muscles to two walls.  The
 * block can make contact with the ground.
 */
int main()
{
    try {

                // Need to load this DLL so muscle types are recognized.
        LoadOpenSimLibrary( "osimActuators" );

        // Create an OpenSim model from the model file provided.
        Model osimModel( "tugOfWar_model_ThelenOnly.osim" );

        // Define the initial and final simulation times.
        double initialTime = 0.0;
        double finalTime = 1.0;

        // Set gain for the controller.
        double kp = 1600.0; // position gain
        double kv = 80.0;   // velocity gain

        // Print the control gains and block mass.
        std::cout << std::endl;
        std::cout << "kp = " << kp << std::endl;
        std::cout << "kv = " << kv << std::endl;

        // Create the controller.
        TugOfWarController *controller = new TugOfWarController(kp, kv);

        // Give the controller the Model's actuators so it knows
        // to control those actuators.
        controller->setActuators( osimModel.updActuators() );

        // Add the controller to the Model.
        osimModel.addController( controller );

        // Initialize the system and get the state representing the
        // system.
        SimTK::State& si = osimModel.initSystem();

        // Define non-zero (defaults are 0) states for the free joint.
        CoordinateSet& modelCoordinateSet =
            osimModel.updCoordinateSet();
        // Get the z translation coordinate.
        Coordinate& zCoord = modelCoordinateSet.
            get( "blockToGround_zTranslation" );
        // Set z translation speed value.
        zCoord.setSpeedValue( si, 0.15 * Pi );

        // Define the initial muscle states.
        const Set<Muscle>& muscleSet = osimModel.getMuscles();
        ActivationFiberLengthMuscle* muscle1 = dynamic_cast<ActivationFiberLengthMuscle*>( &muscleSet.get(0) );
        ActivationFiberLengthMuscle* muscle2 = dynamic_cast<ActivationFiberLengthMuscle*>( &muscleSet.get(1) );
        if((muscle1 == NULL) || (muscle2 == NULL)){
            throw OpenSim::Exception("ControllerExample: muscle1 or muscle2 is not an ActivationFiberLengthMuscle and example cannot proceed.");
        }
        muscle1->setActivation(si, 0.01 ); // muscle1 activation
        muscle1->setFiberLength(si, 0.2 ); // muscle1 fiber length
        muscle2->setActivation(si, 0.01 ); // muscle2 activation
        muscle2->setFiberLength(si, 0.2 ); // muscle2 fiber length

        // Create the integrator and manager for the simulation.
        SimTK::RungeKuttaMersonIntegrator
            integrator( osimModel.getMultibodySystem() );
        integrator.setAccuracy( 1.0e-4 );

        Manager manager( osimModel, integrator );

        // Examine the model.
        osimModel.printDetailedInfo( si, std::cout );

        // Print out the initial position and velocity states.
        for( int i = 0; i < modelCoordinateSet.getSize(); i++ ) {
            std::cout << "Initial " << modelCoordinateSet[i].getName()
                << " = " << modelCoordinateSet[i].getValue( si )
                << ", and speed = "
                << modelCoordinateSet[i].getSpeedValue( si ) << std::endl;
        }

        // Integrate from initial time to final time.
        manager.setInitialTime( initialTime );
        manager.setFinalTime( finalTime );
        std::cout << "\n\nIntegrating from " << initialTime
            << " to " << finalTime << std::endl;
        manager.integrate( si );

        // Save the simulation results.
        auto controlsTable = osimModel.getControlsTable();
        MOTFileAdapter::write(controlsTable, "tugOfWar_controls.mot");

        auto statesTable = manager.getStatesTable();
        MOTFileAdapter::write(statesTable, "tugOfWar_states.mot");
    }
    catch (const std::exception &ex) {

        // In case of an exception, print it out to the screen.
        std::cout << ex.what() << std::endl;

        // Return 1 instead of 0 to indicate that something
        // undesirable happened.
        return 1;
    }

    // If this program executed up to this line, return 0 to
    // indicate that the intended lines of code were executed.
    return 0;
}
<|endoftext|>


<commit_before>/**
 *  @file VPSSMgrFactory.cpp
 *    Definitions for factory to build instances of classes that manage the
 *    calculation of standard state properties for all the species in a phase
 *    (see \ref spthermo and class
 *    \link Cantera::VPSSMgrFactory VPSSMgrFactory\endlink);
 */
/*
 * $Id$
 */

/*
 * Copywrite (2005) Sandia Corporation. Under the terms of
 * Contract DE-AC04-94AL85000 with Sandia Corporation, the
 * U.S. Government retains certain rights in this software.
 */
#ifdef WIN32
#pragma warning(disable:4786)
#endif

#include "SpeciesThermo.h"

#include "VPSSMgr.h"
#include "VPSSMgrFactory.h"

#include "VPStandardStateTP.h"

#include "VPSSMgr_IdealGas.h"
#include "VPSSMgr_ConstVol.h"
#include "VPSSMgr_Water_ConstVol.h"
#include "VPSSMgr_Water_HKFT.h"
#include "VPSSMgr_General.h"

#include "VPSSMgr_types.h"

#include "SpeciesThermoMgr.h"
#include "speciesThermoTypes.h"
#include "SpeciesThermo.h"
#include "SpeciesThermoFactory.h"
#include "GeneralSpeciesThermo.h"

#include "mix_defs.h"

#include "xml.h"
#include "ctml.h"

using namespace ctml;
using namespace std;

namespace Cantera {

  VPSSMgrFactory* VPSSMgrFactory::s_factory = 0;

#if defined(THREAD_SAFE_CANTERA)
  // Defn of the static mutex variable that locks the
  // %VPSSMgr factory singelton
  boost::mutex VPSSMgrFactory::vpss_species_thermo_mutex;
#endif

  //! Examine the types of species thermo parameterizations, and return a flag indicating the type of parameterization
  //! needed by the species.
  /*!
   *
   *  @param spDataNodeList            Species Data XML node. This node contains a list
   *                                     of species XML nodes underneath it.
   *  @param has_nasa_idealGas         Boolean indicating that one species has a nasa ideal gas standard state
   *  @param has_nasa_constVol         Boolean indicating that one species has a nasa ideal solution standard state
   *  @param has_shomate_idealGas      Boolean indicating that one species has a shomate ideal gas standard state
   *  @param has_shomate_constVol      Boolean indicating that one species has a shomate ideal solution standard state
   *  @param has_simple_idealGas       Boolean indicating that one species has a simple ideal gas standard state
   *  @param has_simple_constVol       Boolean indicating that one species has a simple ideal solution standard state
   *  @param has_water                 Boolean indicating that one species has a water standard state
   *  @param has_tpx                   Boolean indicating that one species has a tpx standard state
   *  @param has_hptx                  Boolean indicating that one species has a htpx standard state
   *  @param has_other                 Boolean indicating that one species has different standard state than the ones listed above
   *
   * @todo Make sure that spDadta_node is species Data XML node by checking
   *      its name is speciesData
   */
  static void getVPSSMgrTypes(std::vector<XML_Node *> & spDataNodeList,
			      int &has_nasa_idealGas,
			      int &has_nasa_constVol,
			      int& has_shomate_idealGas,
			      int& has_shomate_constVol,
			      int& has_simple_idealGas,
			      int& has_simple_constVol,
			      int &has_water,
			      int &has_tpx,
			      int &has_hptx,
			      int &has_other) {

    XML_Node *ss_ptr = 0;
    string ssModel = "idealGas";
    size_t ns = spDataNodeList.size();
    for (size_t n = 0; n < ns; n++) {
      bool ifound = false;
      XML_Node* spNode = spDataNodeList[n];
      if (spNode->hasChild("standardState")) {
	const XML_Node& ssN = spNode->child("standardState");
	string mm = ssN["model"];
	if (mm == "waterIAPWS" || mm == "waterPDSS") {
	  has_water++;
	  ifound = true;
	}
	if (mm == "HKFT") {
	  has_hptx++;
	  ifound = true;
	}
      }
      if (!ifound) {
	if (spNode->hasChild("thermo")) {
	  const XML_Node& th = spNode->child("thermo");
	  if (spNode->hasChild("standardState")) {
	    ss_ptr = &(spNode->child("standardState"));
	    ssModel = ss_ptr->attrib("model");
	  }
	  if (th.hasChild("NASA")) {
	    if (ssModel == "idealGas") {
	      has_nasa_idealGas++;
	    } else if (ssModel == "constant_incompressible" ||
		       ssModel == "constantVolume") {
	      has_nasa_constVol++;
	    } else if (ssModel == "temperature_polynomial" ||
		       ssModel == "density_temperature_polynomial"  ||
		       ssModel == "constant") {
	      has_other++;
	    } else {
	      throw UnknownVPSSMgrModel("getVPSSMgrTypes:",
					spNode->attrib("name"));
	    }
	    ifound = true;
	  }
	  if (th.hasChild("Shomate")) {
	    if (ssModel == "idealGas") {
	      has_shomate_idealGas++;
	    } else if (ssModel == "constant_incompressible" ||
		       ssModel == "constantVolume") {
	      has_shomate_constVol++;
	    } else if (ssModel == "temperature_polynomial" ||
		       ssModel == "density_temperature_polynomial"  ||
		       ssModel == "constant") {
	      has_other++;
	    } else {
	      throw UnknownVPSSMgrModel("getVPSSMgrTypes:",
					spNode->attrib("name"));
	    }
	    ifound = true;
	  }
	  if (th.hasChild("const_cp")){
	    if (ssModel == "idealGas") {
	      has_simple_idealGas++;
	    } else if (ssModel == "constant_incompressible" ||
		       ssModel == "constantVolume") {
	      has_simple_constVol++;
	    } else if (ssModel == "temperature_polynomial" ||
		       ssModel == "density_temperature_polynomial"  ||
		       ssModel == "constant") {
	      has_other++;
	    } else {
	      throw UnknownVPSSMgrModel("getVPSSMgrTypes:",
					spNode->attrib("name"));
	    }
	    ifound = true;
	  }
	  if (th.hasChild("poly")) {
	    if (th.child("poly")["order"] == "1") {
	      has_simple_constVol = 1;
	      ifound = true;
	    } else throw CanteraError("newSpeciesThermo",
				      "poly with order > 1 not yet supported");
	  }
	  if (th.hasChild("Mu0")) {
	    has_other++;
	    ifound = true;
	  }
	  if (th.hasChild("NASA9")) {
	    has_other++;
	    ifound = true;
	  }
	  if (th.hasChild("NASA9MULTITEMP")) {
	    has_other++;
	    ifound = true;
	  }
	  if (th.hasChild("adsorbate")) {
	    has_other++;
	    ifound = true;
	  }
	  if (th.hasChild("HKFT")) {
	    has_hptx++;
	    ifound = true;
	  }
	} else {
	  throw UnknownVPSSMgrModel("getVPSSMgrTypes:",
				    spNode->attrib("name"));
	}
      }
    }
  }

  // Delete static instance of this class
  /*
   * If it is necessary to explicitly delete the factory before
   * the process terminates (for example, when checking for
   * memory leaks) then this method can be called to delete it.
   */
  void VPSSMgrFactory::deleteFactory() {

#if defined(THREAD_SAFE_CANTERA)
      boost::mutex::scoped_lock lock(species_thermo_mutex);
#endif
      if (s_factory) {
	delete s_factory;
	s_factory = 0;
      }
  }

  VPSSMgrFactory::~VPSSMgrFactory() {
  }

  VPSSMgr_enumType
  VPSSMgrFactory::VPSSMgr_StringConversion(std::string ssModel) const {
    std::string lssModel = lowercase(ssModel);
    VPSSMgr_enumType type;
    if (lssModel == "idealgas") {
      type = cVPSSMGR_IDEALGAS;
    } else if (lssModel == "constvol") {
      type = cVPSSMGR_CONSTVOL;
    } else if (lssModel == "purefuild") {
      type = cVPSSMGR_PUREFLUID;
    } else if (lssModel == "water_constvol") {
      type = cVPSSMGR_WATER_CONSTVOL;
    } else if (lssModel == "water_hkft") {
      type = cVPSSMGR_WATER_HKFT;
    } else if (lssModel == "general") {
      type = cVPSSMGR_GENERAL;
    } else {
      type = cVPSSMGR_UNDEF;
    }
    return type;
  }

  // Chose the variable pressure standard state manager
  // and the reference standard state manager
  VPSSMgr*
  VPSSMgrFactory::newVPSSMgr(VPStandardStateTP *vp_ptr,
			     XML_Node* phaseNode_ptr,
			     std::vector<XML_Node*> & spDataNodeList) {

    std::string ssManager="";
    std::string vpssManager="";
    VPSSMgr *vpss = 0;

    // First look for any explicit instructions within the XML Database
    // for the standard state manager and the variable pressure
    // standard state manager
    if (phaseNode_ptr) {
      if (phaseNode_ptr->hasChild("thermo")) {
	const XML_Node& thermoNode = phaseNode_ptr->child("thermo");
	if (thermoNode.hasChild("standardStateManager")) {
	  const XML_Node& ssNode = thermoNode.child("standardStateManager");
	  ssManager = ssNode["model"];
	}
	if (thermoNode.hasChild("variablePressureStandardStateManager")) {
	  const XML_Node& vpssNode = thermoNode.child("variablePressureStandardStateManager");
	  vpssManager = vpssNode["model"];
	}
      }
    }

    // first get the reference state handler. If we have explicit instructions,
    // use them to spawn the object.
    SpeciesThermo *spth = 0;
    if (ssManager != "") {
      spth = newSpeciesThermoMgr(ssManager);
    } else {
      spth = newSpeciesThermoMgr(spDataNodeList);
    }
    vp_ptr->setSpeciesThermo(spth);

    // Next, if we have specific directions, use them to get the VPSSSMgr object
    // and return immediately
    if (vpssManager != "") {
      VPSSMgr_enumType type = VPSSMgr_StringConversion(vpssManager);
      vpss = newVPSSMgr(type, vp_ptr);
      return vpss;
    }

    // If it comes back as general, then there may be some unknown
    // parameterizations to the SpeciesThermo factory routine.
    bool haveSomeUnknowns = true;
    GeneralSpeciesThermo *ttmp = dynamic_cast<GeneralSpeciesThermo *>(spth);
    if (ttmp == 0) {
      haveSomeUnknowns = false;
    }

    // Handle special cases based on the VPStandardState types
    if (vp_ptr->eosType() == cVPSS_IdealGas) {
      vpss = new VPSSMgr_IdealGas(vp_ptr, spth);
      return vpss;
    } else if (vp_ptr->eosType() == cVPSS_ConstVol) {
      vpss = new VPSSMgr_ConstVol(vp_ptr, spth);
      return vpss;
    }

    int inasaIG = 0, inasaCV = 0, ishomateIG = 0, ishomateCV = 0,
      isimpleIG = 0, isimpleCV = 0,
      iwater = 0, itpx = 0, iother = 0;
    int ihptx = 0;

    try {
      getVPSSMgrTypes(spDataNodeList, inasaIG, inasaCV, ishomateIG, ishomateCV,
		      isimpleIG, isimpleCV, iwater, itpx, ihptx, iother);
    } catch (UnknownSpeciesThermoModel) {
      iother = 1;
      popError();
    }

    if (iwater == 1) {
      if (ihptx == 0) {
	if (inasaIG ||  ishomateIG || isimpleIG) {
	  throw CanteraError("newVPSSMgr", "Ideal gas with liquid water");
	} else {
	  vpss = new VPSSMgr_Water_ConstVol(vp_ptr, spth);
	}
      } else {
	if (inasaIG ||  ishomateIG || isimpleIG) {
	  throw CanteraError("newVPSSMgr", "Ideal gas with liquid water");
	} else if (inasaCV || ishomateCV ||  isimpleCV) {
	  vpss = new VPSSMgr_General(vp_ptr, spth);
	} else {
	  vpss = new VPSSMgr_Water_HKFT(vp_ptr, spth);
	}
      }
    }
    if (vpss == 0) {
      if (inasaCV || ishomateCV || isimpleCV) {
	if (!inasaIG && !ishomateIG && !isimpleIG && !itpx && !ihptx && !iother) {
	  vpss = new VPSSMgr_ConstVol(vp_ptr, spth);
	}
      }
    }
    if (vpss == 0) {
      vpss = new VPSSMgr_General(vp_ptr, spth);
    }
    return vpss;
  }

  // I don't think this is currently used. However, this is a virtual
  // function where additional capabilities may be added.
  VPSSMgr*
  VPSSMgrFactory::newVPSSMgr(VPSSMgr_enumType type, VPStandardStateTP *vp_ptr) {
    SpeciesThermo &spthermoRef = vp_ptr->speciesThermo();
    switch (type) {
    case cVPSSMGR_IDEALGAS:
      return new VPSSMgr_IdealGas(vp_ptr, &spthermoRef);
      break;
    case cVPSSMGR_CONSTVOL:
      return new VPSSMgr_ConstVol(vp_ptr, &spthermoRef);
      break;
    case cVPSSMGR_PUREFLUID:
      throw CanteraError("VPSSMgrFactory::newVPSSMgr",
			 "unimplemented");
    case cVPSSMGR_WATER_CONSTVOL:
      return new VPSSMgr_Water_ConstVol(vp_ptr, &spthermoRef);
      break;
    case cVPSSMGR_WATER_HKFT:
      return new VPSSMgr_Water_HKFT(vp_ptr, &spthermoRef);
      break;
    case cVPSSMGR_GENERAL:
      return new VPSSMgr_General(vp_ptr, &spthermoRef);
      break;
    case cVPSSMGR_UNDEF:
    default:
      throw UnknownVPSSMgrModel("VPSSMgrFactory::newVPSSMgr", int2str(type));
      return 0;
    }
  }

  // I don't think this is currently used
  VPSSMgr* newVPSSMgr(VPSSMgr_enumType type, VPStandardStateTP *vp_ptr,
		      Cantera::VPSSMgrFactory* f) {
    if (f == 0) {
      f = VPSSMgrFactory::factory();
    }
    VPSSMgr* vpsssptherm = f->newVPSSMgr(type, vp_ptr);
    return vpsssptherm;
  }

  VPSSMgr* newVPSSMgr(VPStandardStateTP *tp_ptr,
		      XML_Node* phaseNode_ptr,
		      std::vector<XML_Node *> & spDataNodeList,
		      VPSSMgrFactory* f) {
    if (f == 0) {
      f = VPSSMgrFactory::factory();
    }
    VPSSMgr* vpsssptherm = f->newVPSSMgr(tp_ptr, phaseNode_ptr, spDataNodeList);
    return vpsssptherm;
  }

}
<commit_msg>Fixed issue #17. There was an obvious error in the THREAD_SAFE_CANTERA ifdef block compile.<commit_after>/**
 *  @file VPSSMgrFactory.cpp
 *    Definitions for factory to build instances of classes that manage the
 *    calculation of standard state properties for all the species in a phase
 *    (see \ref spthermo and class
 *    \link Cantera::VPSSMgrFactory VPSSMgrFactory\endlink);
 */
/*
 * $Id$
 */

/*
 * Copywrite (2005) Sandia Corporation. Under the terms of
 * Contract DE-AC04-94AL85000 with Sandia Corporation, the
 * U.S. Government retains certain rights in this software.
 */
#ifdef WIN32
#pragma warning(disable:4786)
#endif

#include "SpeciesThermo.h"

#include "VPSSMgr.h"
#include "VPSSMgrFactory.h"

#include "VPStandardStateTP.h"

#include "VPSSMgr_IdealGas.h"
#include "VPSSMgr_ConstVol.h"
#include "VPSSMgr_Water_ConstVol.h"
#include "VPSSMgr_Water_HKFT.h"
#include "VPSSMgr_General.h"

#include "VPSSMgr_types.h"

#include "SpeciesThermoMgr.h"
#include "speciesThermoTypes.h"
#include "SpeciesThermo.h"
#include "SpeciesThermoFactory.h"
#include "GeneralSpeciesThermo.h"

#include "mix_defs.h"

#include "xml.h"
#include "ctml.h"

using namespace ctml;
using namespace std;

namespace Cantera {

  VPSSMgrFactory* VPSSMgrFactory::s_factory = 0;

#if defined(THREAD_SAFE_CANTERA)
  // Defn of the static mutex variable that locks the
  // %VPSSMgr factory singelton
  boost::mutex VPSSMgrFactory::vpss_species_thermo_mutex;
#endif

  //! Examine the types of species thermo parameterizations, and return a flag indicating the type of parameterization
  //! needed by the species.
  /*!
   *
   *  @param spDataNodeList            Species Data XML node. This node contains a list
   *                                     of species XML nodes underneath it.
   *  @param has_nasa_idealGas         Boolean indicating that one species has a nasa ideal gas standard state
   *  @param has_nasa_constVol         Boolean indicating that one species has a nasa ideal solution standard state
   *  @param has_shomate_idealGas      Boolean indicating that one species has a shomate ideal gas standard state
   *  @param has_shomate_constVol      Boolean indicating that one species has a shomate ideal solution standard state
   *  @param has_simple_idealGas       Boolean indicating that one species has a simple ideal gas standard state
   *  @param has_simple_constVol       Boolean indicating that one species has a simple ideal solution standard state
   *  @param has_water                 Boolean indicating that one species has a water standard state
   *  @param has_tpx                   Boolean indicating that one species has a tpx standard state
   *  @param has_hptx                  Boolean indicating that one species has a htpx standard state
   *  @param has_other                 Boolean indicating that one species has different standard state than the ones listed above
   *
   * @todo Make sure that spDadta_node is species Data XML node by checking
   *      its name is speciesData
   */
  static void getVPSSMgrTypes(std::vector<XML_Node *> & spDataNodeList,
			      int &has_nasa_idealGas,
			      int &has_nasa_constVol,
			      int& has_shomate_idealGas,
			      int& has_shomate_constVol,
			      int& has_simple_idealGas,
			      int& has_simple_constVol,
			      int &has_water,
			      int &has_tpx,
			      int &has_hptx,
			      int &has_other) {

    XML_Node *ss_ptr = 0;
    string ssModel = "idealGas";
    size_t ns = spDataNodeList.size();
    for (size_t n = 0; n < ns; n++) {
      bool ifound = false;
      XML_Node* spNode = spDataNodeList[n];
      if (spNode->hasChild("standardState")) {
	const XML_Node& ssN = spNode->child("standardState");
	string mm = ssN["model"];
	if (mm == "waterIAPWS" || mm == "waterPDSS") {
	  has_water++;
	  ifound = true;
	}
	if (mm == "HKFT") {
	  has_hptx++;
	  ifound = true;
	}
      }
      if (!ifound) {
	if (spNode->hasChild("thermo")) {
	  const XML_Node& th = spNode->child("thermo");
	  if (spNode->hasChild("standardState")) {
	    ss_ptr = &(spNode->child("standardState"));
	    ssModel = ss_ptr->attrib("model");
	  }
	  if (th.hasChild("NASA")) {
	    if (ssModel == "idealGas") {
	      has_nasa_idealGas++;
	    } else if (ssModel == "constant_incompressible" ||
		       ssModel == "constantVolume") {
	      has_nasa_constVol++;
	    } else if (ssModel == "temperature_polynomial" ||
		       ssModel == "density_temperature_polynomial"  ||
		       ssModel == "constant") {
	      has_other++;
	    } else {
	      throw UnknownVPSSMgrModel("getVPSSMgrTypes:",
					spNode->attrib("name"));
	    }
	    ifound = true;
	  }
	  if (th.hasChild("Shomate")) {
	    if (ssModel == "idealGas") {
	      has_shomate_idealGas++;
	    } else if (ssModel == "constant_incompressible" ||
		       ssModel == "constantVolume") {
	      has_shomate_constVol++;
	    } else if (ssModel == "temperature_polynomial" ||
		       ssModel == "density_temperature_polynomial"  ||
		       ssModel == "constant") {
	      has_other++;
	    } else {
	      throw UnknownVPSSMgrModel("getVPSSMgrTypes:",
					spNode->attrib("name"));
	    }
	    ifound = true;
	  }
	  if (th.hasChild("const_cp")){
	    if (ssModel == "idealGas") {
	      has_simple_idealGas++;
	    } else if (ssModel == "constant_incompressible" ||
		       ssModel == "constantVolume") {
	      has_simple_constVol++;
	    } else if (ssModel == "temperature_polynomial" ||
		       ssModel == "density_temperature_polynomial"  ||
		       ssModel == "constant") {
	      has_other++;
	    } else {
	      throw UnknownVPSSMgrModel("getVPSSMgrTypes:",
					spNode->attrib("name"));
	    }
	    ifound = true;
	  }
	  if (th.hasChild("poly")) {
	    if (th.child("poly")["order"] == "1") {
	      has_simple_constVol = 1;
	      ifound = true;
	    } else throw CanteraError("newSpeciesThermo",
				      "poly with order > 1 not yet supported");
	  }
	  if (th.hasChild("Mu0")) {
	    has_other++;
	    ifound = true;
	  }
	  if (th.hasChild("NASA9")) {
	    has_other++;
	    ifound = true;
	  }
	  if (th.hasChild("NASA9MULTITEMP")) {
	    has_other++;
	    ifound = true;
	  }
	  if (th.hasChild("adsorbate")) {
	    has_other++;
	    ifound = true;
	  }
	  if (th.hasChild("HKFT")) {
	    has_hptx++;
	    ifound = true;
	  }
	} else {
	  throw UnknownVPSSMgrModel("getVPSSMgrTypes:",
				    spNode->attrib("name"));
	}
      }
    }
  }

  // Delete static instance of this class
  /*
   * If it is necessary to explicitly delete the factory before
   * the process terminates (for example, when checking for
   * memory leaks) then this method can be called to delete it.
   */
  void VPSSMgrFactory::deleteFactory() {

#if defined(THREAD_SAFE_CANTERA)
      boost::mutex::scoped_lock lock(vpss_species_thermo_mutex);
#endif
      if (s_factory) {
	delete s_factory;
	s_factory = 0;
      }
  }

  VPSSMgrFactory::~VPSSMgrFactory() {
  }

  VPSSMgr_enumType
  VPSSMgrFactory::VPSSMgr_StringConversion(std::string ssModel) const {
    std::string lssModel = lowercase(ssModel);
    VPSSMgr_enumType type;
    if (lssModel == "idealgas") {
      type = cVPSSMGR_IDEALGAS;
    } else if (lssModel == "constvol") {
      type = cVPSSMGR_CONSTVOL;
    } else if (lssModel == "purefuild") {
      type = cVPSSMGR_PUREFLUID;
    } else if (lssModel == "water_constvol") {
      type = cVPSSMGR_WATER_CONSTVOL;
    } else if (lssModel == "water_hkft") {
      type = cVPSSMGR_WATER_HKFT;
    } else if (lssModel == "general") {
      type = cVPSSMGR_GENERAL;
    } else {
      type = cVPSSMGR_UNDEF;
    }
    return type;
  }

  // Chose the variable pressure standard state manager
  // and the reference standard state manager
  VPSSMgr*
  VPSSMgrFactory::newVPSSMgr(VPStandardStateTP *vp_ptr,
			     XML_Node* phaseNode_ptr,
			     std::vector<XML_Node*> & spDataNodeList) {

    std::string ssManager="";
    std::string vpssManager="";
    VPSSMgr *vpss = 0;

    // First look for any explicit instructions within the XML Database
    // for the standard state manager and the variable pressure
    // standard state manager
    if (phaseNode_ptr) {
      if (phaseNode_ptr->hasChild("thermo")) {
	const XML_Node& thermoNode = phaseNode_ptr->child("thermo");
	if (thermoNode.hasChild("standardStateManager")) {
	  const XML_Node& ssNode = thermoNode.child("standardStateManager");
	  ssManager = ssNode["model"];
	}
	if (thermoNode.hasChild("variablePressureStandardStateManager")) {
	  const XML_Node& vpssNode = thermoNode.child("variablePressureStandardStateManager");
	  vpssManager = vpssNode["model"];
	}
      }
    }

    // first get the reference state handler. If we have explicit instructions,
    // use them to spawn the object.
    SpeciesThermo *spth = 0;
    if (ssManager != "") {
      spth = newSpeciesThermoMgr(ssManager);
    } else {
      spth = newSpeciesThermoMgr(spDataNodeList);
    }
    vp_ptr->setSpeciesThermo(spth);

    // Next, if we have specific directions, use them to get the VPSSSMgr object
    // and return immediately
    if (vpssManager != "") {
      VPSSMgr_enumType type = VPSSMgr_StringConversion(vpssManager);
      vpss = newVPSSMgr(type, vp_ptr);
      return vpss;
    }

    // If it comes back as general, then there may be some unknown
    // parameterizations to the SpeciesThermo factory routine.
    bool haveSomeUnknowns = true;
    GeneralSpeciesThermo *ttmp = dynamic_cast<GeneralSpeciesThermo *>(spth);
    if (ttmp == 0) {
      haveSomeUnknowns = false;
    }

    // Handle special cases based on the VPStandardState types
    if (vp_ptr->eosType() == cVPSS_IdealGas) {
      vpss = new VPSSMgr_IdealGas(vp_ptr, spth);
      return vpss;
    } else if (vp_ptr->eosType() == cVPSS_ConstVol) {
      vpss = new VPSSMgr_ConstVol(vp_ptr, spth);
      return vpss;
    }

    int inasaIG = 0, inasaCV = 0, ishomateIG = 0, ishomateCV = 0,
      isimpleIG = 0, isimpleCV = 0,
      iwater = 0, itpx = 0, iother = 0;
    int ihptx = 0;

    try {
      getVPSSMgrTypes(spDataNodeList, inasaIG, inasaCV, ishomateIG, ishomateCV,
		      isimpleIG, isimpleCV, iwater, itpx, ihptx, iother);
    } catch (UnknownSpeciesThermoModel) {
      iother = 1;
      popError();
    }

    if (iwater == 1) {
      if (ihptx == 0) {
	if (inasaIG ||  ishomateIG || isimpleIG) {
	  throw CanteraError("newVPSSMgr", "Ideal gas with liquid water");
	} else {
	  vpss = new VPSSMgr_Water_ConstVol(vp_ptr, spth);
	}
      } else {
	if (inasaIG ||  ishomateIG || isimpleIG) {
	  throw CanteraError("newVPSSMgr", "Ideal gas with liquid water");
	} else if (inasaCV || ishomateCV ||  isimpleCV) {
	  vpss = new VPSSMgr_General(vp_ptr, spth);
	} else {
	  vpss = new VPSSMgr_Water_HKFT(vp_ptr, spth);
	}
      }
    }
    if (vpss == 0) {
      if (inasaCV || ishomateCV || isimpleCV) {
	if (!inasaIG && !ishomateIG && !isimpleIG && !itpx && !ihptx && !iother) {
	  vpss = new VPSSMgr_ConstVol(vp_ptr, spth);
	}
      }
    }
    if (vpss == 0) {
      vpss = new VPSSMgr_General(vp_ptr, spth);
    }
    return vpss;
  }

  // I don't think this is currently used. However, this is a virtual
  // function where additional capabilities may be added.
  VPSSMgr*
  VPSSMgrFactory::newVPSSMgr(VPSSMgr_enumType type, VPStandardStateTP *vp_ptr) {
    SpeciesThermo &spthermoRef = vp_ptr->speciesThermo();
    switch (type) {
    case cVPSSMGR_IDEALGAS:
      return new VPSSMgr_IdealGas(vp_ptr, &spthermoRef);
      break;
    case cVPSSMGR_CONSTVOL:
      return new VPSSMgr_ConstVol(vp_ptr, &spthermoRef);
      break;
    case cVPSSMGR_PUREFLUID:
      throw CanteraError("VPSSMgrFactory::newVPSSMgr",
			 "unimplemented");
    case cVPSSMGR_WATER_CONSTVOL:
      return new VPSSMgr_Water_ConstVol(vp_ptr, &spthermoRef);
      break;
    case cVPSSMGR_WATER_HKFT:
      return new VPSSMgr_Water_HKFT(vp_ptr, &spthermoRef);
      break;
    case cVPSSMGR_GENERAL:
      return new VPSSMgr_General(vp_ptr, &spthermoRef);
      break;
    case cVPSSMGR_UNDEF:
    default:
      throw UnknownVPSSMgrModel("VPSSMgrFactory::newVPSSMgr", int2str(type));
      return 0;
    }
  }

  // I don't think this is currently used
  VPSSMgr* newVPSSMgr(VPSSMgr_enumType type, VPStandardStateTP *vp_ptr,
		      Cantera::VPSSMgrFactory* f) {
    if (f == 0) {
      f = VPSSMgrFactory::factory();
    }
    VPSSMgr* vpsssptherm = f->newVPSSMgr(type, vp_ptr);
    return vpsssptherm;
  }

  VPSSMgr* newVPSSMgr(VPStandardStateTP *tp_ptr,
		      XML_Node* phaseNode_ptr,
		      std::vector<XML_Node *> & spDataNodeList,
		      VPSSMgrFactory* f) {
    if (f == 0) {
      f = VPSSMgrFactory::factory();
    }
    VPSSMgr* vpsssptherm = f->newVPSSMgr(tp_ptr, phaseNode_ptr, spDataNodeList);
    return vpsssptherm;
  }

}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012-2013 ARM Limited
 * All rights reserved
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Andreas Hansson
 */

/**
 * @file
 * ClockedObject declaration and implementation.
 */

#ifndef __SIM_CLOCKED_OBJECT_HH__
#define __SIM_CLOCKED_OBJECT_HH__

#include "base/intmath.hh"
#include "base/misc.hh"
#include "params/ClockedObject.hh"
#include "sim/core.hh"
#include "sim/clock_domain.hh"
#include "sim/sim_object.hh"

/**
 * The ClockedObject class extends the SimObject with a clock and
 * accessor functions to relate ticks to the cycles of the object.
 */
class ClockedObject : public SimObject
{

  private:

    // the tick value of the next clock edge (>= curTick()) at the
    // time of the last call to update()
    mutable Tick tick;

    // The cycle counter value corresponding to the current value of
    // 'tick'
    mutable Cycles cycle;

    /**
     * Prevent inadvertent use of the copy constructor and assignment
     * operator by making them private.
     */
    ClockedObject(ClockedObject&);
    ClockedObject& operator=(ClockedObject&);

    /**
     *  Align cycle and tick to the next clock edge if not already done.
     */
    void update() const
    {
        // both tick and cycle are up-to-date and we are done, note
        // that the >= is important as it captures cases where tick
        // has already passed curTick()
        if (tick >= curTick())
            return;

        // optimise for the common case and see if the tick should be
        // advanced by a single clock period
        tick += clockPeriod();
        ++cycle;

        // see if we are done at this point
        if (tick >= curTick())
            return;

        // if not, we have to recalculate the cycle and tick, we
        // perform the calculations in terms of relative cycles to
        // allow changes to the clock period in the future
        Cycles elapsedCycles(divCeil(curTick() - tick, clockPeriod()));
        cycle += elapsedCycles;
        tick += elapsedCycles * clockPeriod();
    }

    /**
     * The clock domain this clocked object belongs to
     */
    ClockDomain &clockDomain;

  protected:

    /**
     * Create a clocked object and set the clock domain based on the
     * parameters.
     */
    ClockedObject(const ClockedObjectParams* p) :
        SimObject(p), tick(0), cycle(0), clockDomain(*p->clk_domain)
    {
    }

    /**
     * Virtual destructor due to inheritance.
     */
    virtual ~ClockedObject() { }

    /**
     * Reset the object's clock using the current global tick value. Likely
     * to be used only when the global clock is reset. Currently, this done
     * only when Ruby is done warming up the memory system.
     */
    void resetClock() const
    {
        Cycles elapsedCycles(divCeil(curTick(), clockPeriod()));
        cycle = elapsedCycles;
        tick = elapsedCycles * clockPeriod();
    }

  public:

    /**
     * Determine the tick when a cycle begins, by default the current
     * one, but the argument also enables the caller to determine a
     * future cycle.
     *
     * @param cycles The number of cycles into the future
     *
     * @return The tick when the clock edge occurs
     */
    inline Tick clockEdge(Cycles cycles = Cycles(0)) const
    {
        // align tick to the next clock edge
        update();

        // figure out when this future cycle is
        return tick + clockPeriod() * cycles;
    }

    /**
     * Determine the current cycle, corresponding to a tick aligned to
     * a clock edge.
     *
     * @return The current cycle count
     */
    inline Cycles curCycle() const
    {
        // align cycle to the next clock edge.
        update();

        return cycle;
    }

    /**
     * Based on the clock of the object, determine the tick when the next
     * cycle begins, in other words, return the next clock edge.
     * (This can never be the current tick.)
     *
     * @return The tick when the next cycle starts
     */
    Tick nextCycle() const
    { return clockEdge(Cycles(1)); }

    inline uint64_t frequency() const
    {
        return SimClock::Frequency / clockPeriod();
    }

    inline Tick clockPeriod() const
    {
        return clockDomain.clockPeriod();
    }

    inline Cycles ticksToCycles(Tick t) const
    { return Cycles(t / clockPeriod()); }

};

#endif //__SIM_CLOCKED_OBJECT_HH__
<commit_msg>sim: correct ticksToCycles() function.<commit_after>/*
 * Copyright (c) 2012-2013 ARM Limited
 * All rights reserved
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Andreas Hansson
 */

/**
 * @file
 * ClockedObject declaration and implementation.
 */

#ifndef __SIM_CLOCKED_OBJECT_HH__
#define __SIM_CLOCKED_OBJECT_HH__

#include "base/intmath.hh"
#include "base/misc.hh"
#include "params/ClockedObject.hh"
#include "sim/core.hh"
#include "sim/clock_domain.hh"
#include "sim/sim_object.hh"

/**
 * The ClockedObject class extends the SimObject with a clock and
 * accessor functions to relate ticks to the cycles of the object.
 */
class ClockedObject : public SimObject
{

  private:

    // the tick value of the next clock edge (>= curTick()) at the
    // time of the last call to update()
    mutable Tick tick;

    // The cycle counter value corresponding to the current value of
    // 'tick'
    mutable Cycles cycle;

    /**
     * Prevent inadvertent use of the copy constructor and assignment
     * operator by making them private.
     */
    ClockedObject(ClockedObject&);
    ClockedObject& operator=(ClockedObject&);

    /**
     *  Align cycle and tick to the next clock edge if not already done.
     */
    void update() const
    {
        // both tick and cycle are up-to-date and we are done, note
        // that the >= is important as it captures cases where tick
        // has already passed curTick()
        if (tick >= curTick())
            return;

        // optimise for the common case and see if the tick should be
        // advanced by a single clock period
        tick += clockPeriod();
        ++cycle;

        // see if we are done at this point
        if (tick >= curTick())
            return;

        // if not, we have to recalculate the cycle and tick, we
        // perform the calculations in terms of relative cycles to
        // allow changes to the clock period in the future
        Cycles elapsedCycles(divCeil(curTick() - tick, clockPeriod()));
        cycle += elapsedCycles;
        tick += elapsedCycles * clockPeriod();
    }

    /**
     * The clock domain this clocked object belongs to
     */
    ClockDomain &clockDomain;

  protected:

    /**
     * Create a clocked object and set the clock domain based on the
     * parameters.
     */
    ClockedObject(const ClockedObjectParams* p) :
        SimObject(p), tick(0), cycle(0), clockDomain(*p->clk_domain)
    {
    }

    /**
     * Virtual destructor due to inheritance.
     */
    virtual ~ClockedObject() { }

    /**
     * Reset the object's clock using the current global tick value. Likely
     * to be used only when the global clock is reset. Currently, this done
     * only when Ruby is done warming up the memory system.
     */
    void resetClock() const
    {
        Cycles elapsedCycles(divCeil(curTick(), clockPeriod()));
        cycle = elapsedCycles;
        tick = elapsedCycles * clockPeriod();
    }

  public:

    /**
     * Determine the tick when a cycle begins, by default the current
     * one, but the argument also enables the caller to determine a
     * future cycle.
     *
     * @param cycles The number of cycles into the future
     *
     * @return The tick when the clock edge occurs
     */
    inline Tick clockEdge(Cycles cycles = Cycles(0)) const
    {
        // align tick to the next clock edge
        update();

        // figure out when this future cycle is
        return tick + clockPeriod() * cycles;
    }

    /**
     * Determine the current cycle, corresponding to a tick aligned to
     * a clock edge.
     *
     * @return The current cycle count
     */
    inline Cycles curCycle() const
    {
        // align cycle to the next clock edge.
        update();

        return cycle;
    }

    /**
     * Based on the clock of the object, determine the tick when the next
     * cycle begins, in other words, return the next clock edge.
     * (This can never be the current tick.)
     *
     * @return The tick when the next cycle starts
     */
    Tick nextCycle() const
    { return clockEdge(Cycles(1)); }

    inline uint64_t frequency() const
    {
        return SimClock::Frequency / clockPeriod();
    }

    inline Tick clockPeriod() const
    {
        return clockDomain.clockPeriod();
    }

    inline Cycles ticksToCycles(Tick t) const
    { return Cycles(divCeil(t, clockPeriod())); }

};

#endif //__SIM_CLOCKED_OBJECT_HH__
<|endoftext|>


<commit_before>/*  Copyright (c) MediaArea.net SARL. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license that can
 *  be found in the License.html file in the root of the source tree.
 */

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// Utility methods for TinyXML-2
// Contributor: Peter Chapman, pchapman@vidcheck.com
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//---------------------------------------------------------------------------
#include "MediaInfo/XmlUtils.h"
#include <string>
#include <cstddef>
using namespace tinyxml2;
//---------------------------------------------------------------------------

// Finds the value of the named attribute within the given element or its
// closest ancestor
static const char *AncestorAttrVal(const XMLElement* Elem, const char* AttrName)
{
    if (!Elem)
        return NULL;
    if (const XMLAttribute* Attr=Elem->FindAttribute(AttrName))
        return Attr->Value();
    const XMLElement* ParentElem=NULL;
    if (const XMLNode* ParentNode=Elem->Parent())
        ParentElem=ParentNode->ToElement();
    return AncestorAttrVal(ParentElem, AttrName);
}

namespace MediaInfoLib
{

//---------------------------------------------------------------------------

const char *LocalName(const XMLElement* Elem)
{
    const char* Name=Elem->Name();
    const char* Colon=strrchr(Name, ':');
    return Colon ? Colon+1 : Name;
}

//---------------------------------------------------------------------------

const char *LocalName(const XMLElement* Elem, const char* &Ns /* out */)
{
    const char* Name=Elem->Name();
    if (const char* Colon=strrchr(Name, ':'))
    {
        // Search element and ancestors for given namespace prefix
        Ns=AncestorAttrVal(Elem, ("xmlns:"+std::string(Name, Colon)).c_str());
        // NB no default namespace if given prefix not found, will return NULL
        return Colon+1;
    }
    else
    {
        Ns=AncestorAttrVal(Elem, "xmlns");
        if (!Ns)
            Ns = ""; // default namespace
        return Name;
    }
}

//---------------------------------------------------------------------------

bool MatchQName(const XMLElement* Elem, const char* Name, const char* NameSpace)
{
    if (strcmp(LocalName(Elem), Name))
        return false; // exit cheaply if local name doesn't match
    const char *ElemNs;
    LocalName(Elem, ElemNs); // look up namespace
    return ElemNs && // ElemNs can be null if Elem uses a bad namespace prefix
           !strcmp(ElemNs, NameSpace); // compare namespaces
}

}
<commit_msg>+ XML utility methods to assist with namespace matching in TinyXML-2, precompilation was failing with Visual C++<commit_after>/*  Copyright (c) MediaArea.net SARL. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license that can
 *  be found in the License.html file in the root of the source tree.
 */

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// Utility methods for TinyXML-2
// Contributor: Peter Chapman, pchapman@vidcheck.com
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//---------------------------------------------------------------------------
// Pre-compilation
#include "MediaInfo/PreComp.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
#include "MediaInfo/XmlUtils.h"
#include <string>
#include <cstddef>
using namespace tinyxml2;
//---------------------------------------------------------------------------

// Finds the value of the named attribute within the given element or its
// closest ancestor
static const char *AncestorAttrVal(const XMLElement* Elem, const char* AttrName)
{
    if (!Elem)
        return NULL;
    if (const XMLAttribute* Attr=Elem->FindAttribute(AttrName))
        return Attr->Value();
    const XMLElement* ParentElem=NULL;
    if (const XMLNode* ParentNode=Elem->Parent())
        ParentElem=ParentNode->ToElement();
    return AncestorAttrVal(ParentElem, AttrName);
}

namespace MediaInfoLib
{

//---------------------------------------------------------------------------

const char *LocalName(const XMLElement* Elem)
{
    const char* Name=Elem->Name();
    const char* Colon=strrchr(Name, ':');
    return Colon ? Colon+1 : Name;
}

//---------------------------------------------------------------------------

const char *LocalName(const XMLElement* Elem, const char* &Ns /* out */)
{
    const char* Name=Elem->Name();
    if (const char* Colon=strrchr(Name, ':'))
    {
        // Search element and ancestors for given namespace prefix
        Ns=AncestorAttrVal(Elem, ("xmlns:"+std::string(Name, Colon)).c_str());
        // NB no default namespace if given prefix not found, will return NULL
        return Colon+1;
    }
    else
    {
        Ns=AncestorAttrVal(Elem, "xmlns");
        if (!Ns)
            Ns = ""; // default namespace
        return Name;
    }
}

//---------------------------------------------------------------------------

bool MatchQName(const XMLElement* Elem, const char* Name, const char* NameSpace)
{
    if (strcmp(LocalName(Elem), Name))
        return false; // exit cheaply if local name doesn't match
    const char *ElemNs;
    LocalName(Elem, ElemNs); // look up namespace
    return ElemNs && // ElemNs can be null if Elem uses a bad namespace prefix
           !strcmp(ElemNs, NameSpace); // compare namespaces
}

}
<|endoftext|>