<commit_before>#include <gtest/gtest.h>

#include "../../cvmfs/util_concurrency.h"

class DummyLocker {
 public:
  DummyLocker() : locked(false) {}
  void Lock() const {
    locked = true;
  }
  void Unlock() const {
    locked = false;
  }

 public:
  mutable bool locked;
};

TEST(T_UtilConcurrency, ArbitraryLockGurad) {
  DummyLocker locker;
  ASSERT_FALSE (locker.locked);

  {
    LockGuard<DummyLocker> lock(locker);
    EXPECT_TRUE (locker.locked) << "LockGuard didn't lock";
  }

  EXPECT_FALSE (locker.locked) << "LockGuard didn't unlock";
}

TEST(T_UtilConcurrency, MutexLockGuard) {
  pthread_mutex_t mutex;
  int retcode = pthread_mutex_init(&mutex, NULL);
  ASSERT_EQ (0, retcode);

  {
    MutexLockGuard lock(mutex);

    retcode = pthread_mutex_trylock(&mutex);
    EXPECT_EQ (EBUSY, retcode) << "MutexLockGuard didn't lock";
  }

  retcode = pthread_mutex_trylock(&mutex);
  EXPECT_EQ (0, retcode) << "MutexLockGuard didn't unlock";

  retcode = pthread_mutex_unlock(&mutex);
  EXPECT_EQ (0, retcode);

  retcode = pthread_mutex_destroy(&mutex);
  EXPECT_EQ (0, retcode);
}

TEST(T_UtilConcurrency, ReadLockGuard) {
  pthread_rwlock_t rwlock;
  int retcode = pthread_rwlock_init(&rwlock, NULL);
  ASSERT_EQ (0, retcode);

  {
    ReadLockGuard lock(rwlock);

    retcode = pthread_rwlock_tryrdlock(&rwlock);
    EXPECT_EQ (0, retcode) << "ReadLockGuard prevents additional read lock";

    retcode = pthread_rwlock_unlock(&rwlock);
    EXPECT_EQ (0, retcode);

    retcode = pthread_rwlock_trywrlock(&rwlock);
    EXPECT_EQ (EBUSY, retcode) << "ReadLockGuard allows concurrent read and write";
  }

  retcode = pthread_rwlock_trywrlock(&rwlock);
  EXPECT_EQ (0, retcode) << "ReadLockGuard didn't unlock";

  retcode = pthread_rwlock_unlock(&rwlock);
  EXPECT_EQ (0, retcode);

  retcode = pthread_rwlock_destroy(&rwlock);
  EXPECT_EQ (0, retcode);
}

TEST(T_UtilConcurrency, WriteLockGuard) {
  pthread_rwlock_t rwlock;
  int retcode = pthread_rwlock_init(&rwlock, NULL);
  ASSERT_EQ (0, retcode);

  {
    WriteLockGuard lock(rwlock);

    retcode = pthread_rwlock_tryrdlock(&rwlock);
    EXPECT_EQ (EBUSY, retcode) << "WriteLockGuard didn't lock - rdlock possible";
    if (0 == retcode) {
      retcode = pthread_rwlock_unlock(&rwlock);
      ASSERT_EQ (0, retcode);
    }

    retcode = pthread_rwlock_trywrlock(&rwlock);
    EXPECT_EQ (EBUSY, retcode) << "WriteLockGuard didn't lock - wrlock possible";
    if (0 == retcode) {
      retcode = pthread_rwlock_unlock(&rwlock);
      ASSERT_EQ (0, retcode);
    }
  }

  retcode = pthread_rwlock_trywrlock(&rwlock);
  EXPECT_EQ (0, retcode) << "WriteLockGuard didn't unlock";

  retcode = pthread_rwlock_unlock(&rwlock);
  EXPECT_EQ (0, retcode);

  retcode = pthread_rwlock_destroy(&rwlock);
  EXPECT_EQ (0, retcode);
}

class DummyLockable : public Lockable {};

TEST(T_UtilConcurrency, Lockable) {
  DummyLockable lockable;
  int retcode = 0;

  {
    LockGuard<DummyLockable> lock(lockable);

    retcode = lockable.TryLock();
    EXPECT_EQ (EBUSY, retcode) << "Lockable didn't lock";
  }

  retcode = lockable.TryLock();
  EXPECT_EQ (0, retcode) << "Lockable didn't unlock";
}
<commit_msg>add tests for basic callback functionality<commit_after>#include <gtest/gtest.h>

#include "../../cvmfs/util_concurrency.h"

class DummyLocker {
 public:
  DummyLocker() : locked(false) {}
  void Lock() const {
    locked = true;
  }
  void Unlock() const {
    locked = false;
  }

 public:
  mutable bool locked;
};

TEST(T_UtilConcurrency, ArbitraryLockGurad) {
  DummyLocker locker;
  ASSERT_FALSE (locker.locked);

  {
    LockGuard<DummyLocker> lock(locker);
    EXPECT_TRUE (locker.locked) << "LockGuard didn't lock";
  }

  EXPECT_FALSE (locker.locked) << "LockGuard didn't unlock";
}

TEST(T_UtilConcurrency, MutexLockGuard) {
  pthread_mutex_t mutex;
  int retcode = pthread_mutex_init(&mutex, NULL);
  ASSERT_EQ (0, retcode);

  {
    MutexLockGuard lock(mutex);

    retcode = pthread_mutex_trylock(&mutex);
    EXPECT_EQ (EBUSY, retcode) << "MutexLockGuard didn't lock";
  }

  retcode = pthread_mutex_trylock(&mutex);
  EXPECT_EQ (0, retcode) << "MutexLockGuard didn't unlock";

  retcode = pthread_mutex_unlock(&mutex);
  EXPECT_EQ (0, retcode);

  retcode = pthread_mutex_destroy(&mutex);
  EXPECT_EQ (0, retcode);
}

TEST(T_UtilConcurrency, ReadLockGuard) {
  pthread_rwlock_t rwlock;
  int retcode = pthread_rwlock_init(&rwlock, NULL);
  ASSERT_EQ (0, retcode);

  {
    ReadLockGuard lock(rwlock);

    retcode = pthread_rwlock_tryrdlock(&rwlock);
    EXPECT_EQ (0, retcode) << "ReadLockGuard prevents additional read lock";

    retcode = pthread_rwlock_unlock(&rwlock);
    EXPECT_EQ (0, retcode);

    retcode = pthread_rwlock_trywrlock(&rwlock);
    EXPECT_EQ (EBUSY, retcode) << "ReadLockGuard allows concurrent read and write";
  }

  retcode = pthread_rwlock_trywrlock(&rwlock);
  EXPECT_EQ (0, retcode) << "ReadLockGuard didn't unlock";

  retcode = pthread_rwlock_unlock(&rwlock);
  EXPECT_EQ (0, retcode);

  retcode = pthread_rwlock_destroy(&rwlock);
  EXPECT_EQ (0, retcode);
}

TEST(T_UtilConcurrency, WriteLockGuard) {
  pthread_rwlock_t rwlock;
  int retcode = pthread_rwlock_init(&rwlock, NULL);
  ASSERT_EQ (0, retcode);

  {
    WriteLockGuard lock(rwlock);

    retcode = pthread_rwlock_tryrdlock(&rwlock);
    EXPECT_EQ (EBUSY, retcode) << "WriteLockGuard didn't lock - rdlock possible";
    if (0 == retcode) {
      retcode = pthread_rwlock_unlock(&rwlock);
      ASSERT_EQ (0, retcode);
    }

    retcode = pthread_rwlock_trywrlock(&rwlock);
    EXPECT_EQ (EBUSY, retcode) << "WriteLockGuard didn't lock - wrlock possible";
    if (0 == retcode) {
      retcode = pthread_rwlock_unlock(&rwlock);
      ASSERT_EQ (0, retcode);
    }
  }

  retcode = pthread_rwlock_trywrlock(&rwlock);
  EXPECT_EQ (0, retcode) << "WriteLockGuard didn't unlock";

  retcode = pthread_rwlock_unlock(&rwlock);
  EXPECT_EQ (0, retcode);

  retcode = pthread_rwlock_destroy(&rwlock);
  EXPECT_EQ (0, retcode);
}

class DummyLockable : public Lockable {};

TEST(T_UtilConcurrency, Lockable) {
  DummyLockable lockable;
  int retcode = 0;

  {
    LockGuard<DummyLockable> lock(lockable);

    retcode = lockable.TryLock();
    EXPECT_EQ (EBUSY, retcode) << "Lockable didn't lock";
  }

  retcode = lockable.TryLock();
  EXPECT_EQ (0, retcode) << "Lockable didn't unlock";
}

void CallbackFn(bool* const &param) { *param = true; }

TEST(T_UtilConcurrency, SimpleCallback) {
  bool callback_called = false;

  Callback<bool*> callback(&CallbackFn);
  callback(&callback_called);
  EXPECT_TRUE (callback_called);
}

class DummyCallbackDelegate {
 public:
  DummyCallbackDelegate() : callback_result(-1) {}
  void CallbackMd(const int &value) { callback_result = value; }

 public:
  int callback_result;
};

TEST(T_UtilConcurrency, BoundCallback) {
  DummyCallbackDelegate delegate;
  ASSERT_EQ (-1, delegate.callback_result);

  BoundCallback<int, DummyCallbackDelegate> callback(
                              &DummyCallbackDelegate::CallbackMd,
                              &delegate);
  callback(42);
  EXPECT_EQ (42, delegate.callback_result);
}

class DummyCallbackable : public Callbackable<int> {
 public:
  DummyCallbackable() : callback_result(-1) {}
  void CallbackMd(const int &value) { callback_result = value; }

 public:
  int callback_result;
};

TEST(T_UtilConcurrency, Callbackable) {
  DummyCallbackable callbackable;
  ASSERT_EQ (-1, callbackable.callback_result);

  DummyCallbackable::callback_t *callback =
    DummyCallbackable::MakeCallback(&DummyCallbackable::CallbackMd,
                                    &callbackable);
  (*callback)(1337);

  EXPECT_EQ (1337, callbackable.callback_result);
}

<|endoftext|>


<commit_before>/*!
  \copyright (c) RDO-Team, 2013
  \file      app/rdo_studio/src/plugins/plugin_loader.cpp
  \author    Чернов Алексей (ChernovAlexeyOlegovich@gmail.com)
  \date      22.09.2013
  \brief
  \indent    4T
*/

// ---------------------------------------------------------------------------- PCH
// ----------------------------------------------------------------------- INCLUDES
#include "utils/src/common/warning_disable.h"
#include <QDir>
#include <QApplication>
#include <QSettings>
#include <boost/foreach.hpp>
#include "utils/src/common/warning_enable.h"
// ----------------------------------------------------------------------- SYNOPSIS
#include "app/rdo_studio/src/plugins/plugin_loader.h"
// --------------------------------------------------------------------------------

using namespace rdo::plugin;

Loader::Loader()
	: m_pPluginsParent(NULL)
	, m_pMergedPluginInfoList(rdo::Factory<PluginInfoList>::create(getMergedPluginInfoList()))
{}

Loader::~Loader()
{
	BOOST_FOREACH(const LPPluginInfo& pluginInfo, *m_pMergedPluginInfoList)
	{
		if (pluginInfo->isActive())
			stopPlugin(pluginInfo);
	}
}

PluginInfoList Loader::getMergedPluginInfoList() const
{
	PluginInfoList pluginHistory = getPluginsHistory();
	PluginInfoList currentLoadedPlugin = getCurrentPlugins();
	PluginInfoList mergedPlugin;
	BOOST_FOREACH(const LPPluginInfo& pluginInfo, currentLoadedPlugin)
	{
		matchPluginInfo(pluginHistory, pluginInfo);
		mergedPlugin.push_back(pluginInfo);
	}
	BOOST_FOREACH(const LPPluginInfo& pluginInfo, pluginHistory)
	{
		if (matchPluginInfo(mergedPlugin, pluginInfo) != rdo::plugin::ExactMatched)
		{
			pluginInfo->setState(rdo::plugin::Deleted);
			mergedPlugin.push_back(pluginInfo);
		}
	}
	return mergedPlugin;
}

PluginInfoList Loader::getPluginsHistory() const
{
	QSettings settings("RAO-studio", "RAO-studio");
	PluginInfoList list;
	int size = settings.beginReadArray("plugins");
	for (int i = 0; i < size; ++i)
	{
		settings.setArrayIndex(i);
		QString pluginName     = settings.value("pluginName"    , "").toString();
		bool    pluginAutoLoad = settings.value("pluginAutoLoad", false).toBool();
		QString pluginAuthor   = settings.value("pluginAuthor"  , "").toString();
		QString pluginVer      = settings.value("pluginVer"     , "").toString();
		QUuid   pluginGUID     = settings.value("pluginGUID"    , QUuid()).toUuid();
		PluginInfo pluginInfo(pluginName, NULL, pluginAutoLoad, pluginGUID, pluginAuthor, pluginVer, rdo::plugin::Unique);
		list.push_back(rdo::Factory<PluginInfo>::create(pluginInfo));
	}
	return list;
}

void Loader::setPluginInfoList(const PluginInfoList& value) const
{
	QSettings settings;
	settings.remove("plugins");
	settings.beginWriteArray("plugins");
	int index = 0;
	BOOST_FOREACH(const LPPluginInfo& pluginInfo, value)
	{
		if (pluginInfo->getState() != rdo::plugin::IdOnlyMatched)
		{
			settings.setArrayIndex(index);
			settings.setValue("pluginName"    , pluginInfo->getName());
			settings.setValue("pluginAutoLoad", pluginInfo->getAutoload());
			settings.setValue("pluginGUID"    , pluginInfo->getGUID());
			settings.setValue("pluginAuthor"  , pluginInfo->getAuthor());
			settings.setValue("pluginVer"     , pluginInfo->getVersion());
			index++;
		}
	}
	settings.endArray();
}

PluginInfoList Loader::getCurrentPlugins() const
{
	PluginInfoList list;
	QDir dir(qApp->applicationDirPath());
	if (dir.cd("plugins"))
	{
		QStringList fileList = getFileList(dir.path());
		BOOST_FOREACH(const QString& filePath, fileList)
		{
			QPluginLoader* pluginLoader = new QPluginLoader(filePath);
			PluginInterface* pluginInterface = loadPlugin(pluginLoader);
			if (pluginInterface)
			{
				PluginInfo pluginInfo = generatePluginInfo(pluginInterface, pluginLoader);
				LPPluginInfo pPlgnInfo = rdo::Factory<PluginInfo>::create(pluginInfo);
				pluginLoader->unload();
				if (matchPluginInfo(list, pPlgnInfo) != rdo::plugin::ExactMatched)
				{
					list.push_back(pPlgnInfo);
				}
				else
				{
					delete pluginLoader;
				}
			}
		}
	}
	return list;
}

int Loader::matchPluginInfo(const PluginInfoList& list, const LPPluginInfo& pluginInfo) const
{
	int pluginState = pluginInfo->getState();
	BOOST_FOREACH(const LPPluginInfo& pluginInfo, list)
	{
		if (pluginInfo->getGUID() == pluginInfo->getGUID())
		{
			if (pluginInfo->pluginSignInfoIsEqual(*pluginInfo))
			{
				pluginInfo->setAutoload(pluginInfo->getAutoload());
				pluginState = rdo::plugin::ExactMatched;
				break;
			}
			else
			{
				pluginState = rdo::plugin::IdOnlyMatched;
			}
		}
	}
	pluginInfo->setState(pluginState);
	return pluginState;
}

QStringList Loader::getFileList(const QString& startDir) const
{
	QDir dir(startDir);
	QStringList list;

	BOOST_FOREACH(const QString& file, dir.entryList(QDir::Files))
	{
		list += dir.absoluteFilePath(file);
	}

	BOOST_FOREACH(const QString& subdir, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
	{
		list += getFileList(startDir + "/" + subdir);
	}
	return list;
}

PluginInterface* Loader::loadPlugin(QPluginLoader* pluginLoader) const
{
	QObject* plugin = pluginLoader->instance();
	if (plugin)
	{
		PluginInterface* pluginInterface = qobject_cast<PluginInterface*>(plugin);
		return pluginInterface;
	}
	return NULL;
}

PluginInfo Loader::generatePluginInfo(PluginInterface* pluginInterface, QPluginLoader* pluginLoader) const
{
	QString pluginName     = pluginInterface->getPluginName();
	QUuid   pluginGUID     = pluginInterface->getGUID();
	QString pluginAuthor   = pluginInterface->getAuthor();
	QString pluginVersion  = pluginInterface->getVersion();
	bool    pluginAutoload = false;
	PluginInfo pluginInfo(pluginName, pluginLoader, pluginAutoload, pluginGUID, pluginAuthor, pluginVersion, rdo::plugin::Unique);
	return pluginInfo;
}

void Loader::stopPlugin(const LPPluginInfo& pluginInfo)
{
	PluginInterface* pluginInterface = loadPlugin(pluginInfo->getLoader());
	if (pluginInterface)
	{
		pluginInterface->pluginStopAction(m_pPluginsParent);
		pluginInfo->setActive(false);
	}
}

void Loader::startPlugin(const LPPluginInfo& pluginInfo)
{
	PluginInterface* pluginInterface = loadPlugin(pluginInfo->getLoader());
	if (pluginInterface)
	{
		pluginInterface->pluginStartAction(m_pPluginsParent);
		pluginInfo->setActive(true);
	}
}

void Loader::startAutoloadedPlugins()
{
	BOOST_FOREACH(const LPPluginInfo& pluginInfo, *m_pMergedPluginInfoList)
	{
		if (pluginInfo->getAutoload() && pluginInfo->isAvailable())
		{
			startPlugin(pluginInfo);
		}
	}
}

const LPPluginInfoList& Loader::getPluginInfoList() const
{
	return m_pMergedPluginInfoList;
}

void Loader::initPluginParent(QWidget* pParent)
{
	ASSERT(!m_pPluginsParent);
	ASSERT(pParent);
	m_pPluginsParent = pParent;
}
<commit_msg> - исправлена критичная опечатка в теле функции matchPluginInfo<commit_after>/*!
  \copyright (c) RDO-Team, 2013
  \file      app/rdo_studio/src/plugins/plugin_loader.cpp
  \author    Чернов Алексей (ChernovAlexeyOlegovich@gmail.com)
  \date      22.09.2013
  \brief
  \indent    4T
*/

// ---------------------------------------------------------------------------- PCH
// ----------------------------------------------------------------------- INCLUDES
#include "utils/src/common/warning_disable.h"
#include <QDir>
#include <QApplication>
#include <QSettings>
#include <boost/foreach.hpp>
#include "utils/src/common/warning_enable.h"
// ----------------------------------------------------------------------- SYNOPSIS
#include "app/rdo_studio/src/plugins/plugin_loader.h"
// --------------------------------------------------------------------------------

using namespace rdo::plugin;

Loader::Loader()
	: m_pPluginsParent(NULL)
	, m_pMergedPluginInfoList(rdo::Factory<PluginInfoList>::create(getMergedPluginInfoList()))
{}

Loader::~Loader()
{
	BOOST_FOREACH(const LPPluginInfo& pluginInfo, *m_pMergedPluginInfoList)
	{
		if (pluginInfo->isActive())
			stopPlugin(pluginInfo);
	}
}

PluginInfoList Loader::getMergedPluginInfoList() const
{
	PluginInfoList pluginHistory = getPluginsHistory();
	PluginInfoList currentLoadedPlugin = getCurrentPlugins();
	PluginInfoList mergedPlugin;
	BOOST_FOREACH(const LPPluginInfo& pluginInfo, currentLoadedPlugin)
	{
		matchPluginInfo(pluginHistory, pluginInfo);
		mergedPlugin.push_back(pluginInfo);
	}
	BOOST_FOREACH(const LPPluginInfo& pluginInfo, pluginHistory)
	{
		if (matchPluginInfo(mergedPlugin, pluginInfo) != rdo::plugin::ExactMatched)
		{
			pluginInfo->setState(rdo::plugin::Deleted);
			mergedPlugin.push_back(pluginInfo);
		}
	}
	return mergedPlugin;
}

PluginInfoList Loader::getPluginsHistory() const
{
	QSettings settings("RAO-studio", "RAO-studio");
	PluginInfoList list;
	int size = settings.beginReadArray("plugins");
	for (int i = 0; i < size; ++i)
	{
		settings.setArrayIndex(i);
		QString pluginName     = settings.value("pluginName"    , "").toString();
		bool    pluginAutoLoad = settings.value("pluginAutoLoad", false).toBool();
		QString pluginAuthor   = settings.value("pluginAuthor"  , "").toString();
		QString pluginVer      = settings.value("pluginVer"     , "").toString();
		QUuid   pluginGUID     = settings.value("pluginGUID"    , QUuid()).toUuid();
		PluginInfo pluginInfo(pluginName, NULL, pluginAutoLoad, pluginGUID, pluginAuthor, pluginVer, rdo::plugin::Unique);
		list.push_back(rdo::Factory<PluginInfo>::create(pluginInfo));
	}
	return list;
}

void Loader::setPluginInfoList(const PluginInfoList& value) const
{
	QSettings settings;
	settings.remove("plugins");
	settings.beginWriteArray("plugins");
	int index = 0;
	BOOST_FOREACH(const LPPluginInfo& pluginInfo, value)
	{
		if (pluginInfo->getState() != rdo::plugin::IdOnlyMatched)
		{
			settings.setArrayIndex(index);
			settings.setValue("pluginName"    , pluginInfo->getName());
			settings.setValue("pluginAutoLoad", pluginInfo->getAutoload());
			settings.setValue("pluginGUID"    , pluginInfo->getGUID());
			settings.setValue("pluginAuthor"  , pluginInfo->getAuthor());
			settings.setValue("pluginVer"     , pluginInfo->getVersion());
			index++;
		}
	}
	settings.endArray();
}

PluginInfoList Loader::getCurrentPlugins() const
{
	PluginInfoList list;
	QDir dir(qApp->applicationDirPath());
	if (dir.cd("plugins"))
	{
		QStringList fileList = getFileList(dir.path());
		BOOST_FOREACH(const QString& filePath, fileList)
		{
			QPluginLoader* pluginLoader = new QPluginLoader(filePath);
			PluginInterface* pluginInterface = loadPlugin(pluginLoader);
			if (pluginInterface)
			{
				PluginInfo pluginInfo = generatePluginInfo(pluginInterface, pluginLoader);
				LPPluginInfo pPlgnInfo = rdo::Factory<PluginInfo>::create(pluginInfo);
				pluginLoader->unload();
				if (matchPluginInfo(list, pPlgnInfo) != rdo::plugin::ExactMatched)
				{
					list.push_back(pPlgnInfo);
				}
				else
				{
					delete pluginLoader;
				}
			}
		}
	}
	return list;
}

int Loader::matchPluginInfo(const PluginInfoList& list, const LPPluginInfo& matchingPluginInfo) const
{
	int pluginState = matchingPluginInfo->getState();
	BOOST_FOREACH(const LPPluginInfo& pluginInfo, list)
	{
		if (matchingPluginInfo->getGUID() == pluginInfo->getGUID())
		{
			if (matchingPluginInfo->pluginSignInfoIsEqual(*pluginInfo))
			{
				matchingPluginInfo->setAutoload(pluginInfo->getAutoload());
				pluginState = rdo::plugin::ExactMatched;
				break;
			}
			else
			{
				pluginState = rdo::plugin::IdOnlyMatched;
			}
		}
	}
	matchingPluginInfo->setState(pluginState);
	return pluginState;
}

QStringList Loader::getFileList(const QString& startDir) const
{
	QDir dir(startDir);
	QStringList list;

	BOOST_FOREACH(const QString& file, dir.entryList(QDir::Files))
	{
		list += dir.absoluteFilePath(file);
	}

	BOOST_FOREACH(const QString& subdir, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
	{
		list += getFileList(startDir + "/" + subdir);
	}
	return list;
}

PluginInterface* Loader::loadPlugin(QPluginLoader* pluginLoader) const
{
	QObject* plugin = pluginLoader->instance();
	if (plugin)
	{
		PluginInterface* pluginInterface = qobject_cast<PluginInterface*>(plugin);
		return pluginInterface;
	}
	return NULL;
}

PluginInfo Loader::generatePluginInfo(PluginInterface* pluginInterface, QPluginLoader* pluginLoader) const
{
	QString pluginName     = pluginInterface->getPluginName();
	QUuid   pluginGUID     = pluginInterface->getGUID();
	QString pluginAuthor   = pluginInterface->getAuthor();
	QString pluginVersion  = pluginInterface->getVersion();
	bool    pluginAutoload = false;
	PluginInfo pluginInfo(pluginName, pluginLoader, pluginAutoload, pluginGUID, pluginAuthor, pluginVersion, rdo::plugin::Unique);
	return pluginInfo;
}

void Loader::stopPlugin(const LPPluginInfo& pluginInfo)
{
	PluginInterface* pluginInterface = loadPlugin(pluginInfo->getLoader());
	if (pluginInterface)
	{
		pluginInterface->pluginStopAction(m_pPluginsParent);
		pluginInfo->setActive(false);
	}
}

void Loader::startPlugin(const LPPluginInfo& pluginInfo)
{
	PluginInterface* pluginInterface = loadPlugin(pluginInfo->getLoader());
	if (pluginInterface)
	{
		pluginInterface->pluginStartAction(m_pPluginsParent);
		pluginInfo->setActive(true);
	}
}

void Loader::startAutoloadedPlugins()
{
	BOOST_FOREACH(const LPPluginInfo& pluginInfo, *m_pMergedPluginInfoList)
	{
		if (pluginInfo->getAutoload() && pluginInfo->isAvailable())
		{
			startPlugin(pluginInfo);
		}
	}
}

const LPPluginInfoList& Loader::getPluginInfoList() const
{
	return m_pMergedPluginInfoList;
}

void Loader::initPluginParent(QWidget* pParent)
{
	ASSERT(!m_pPluginsParent);
	ASSERT(pParent);
	m_pPluginsParent = pParent;
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2003-2012, Arvid Norberg
All rights reserved.

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 author 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.

*/

#include "libtorrent/pch.hpp"

#include <vector>
#include <cctype>

#include <boost/bind.hpp>

#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/http_tracker_connection.hpp"
#include "libtorrent/udp_tracker_connection.hpp"
#include "libtorrent/aux_/session_impl.hpp"

using boost::tuples::make_tuple;
using boost::tuples::tuple;

namespace
{
	enum
	{
		minimum_tracker_response_length = 3,
		http_buffer_size = 2048
	};

}

namespace libtorrent
{
	timeout_handler::timeout_handler(io_service& ios)
		: m_start_time(time_now_hires())
		, m_read_time(m_start_time)
		, m_timeout(ios)
		, m_completion_timeout(0)
		, m_read_timeout(0)
		, m_abort(false)
	{}

	void timeout_handler::set_timeout(int completion_timeout, int read_timeout)
	{
		m_completion_timeout = completion_timeout;
		m_read_timeout = read_timeout;
		m_start_time = m_read_time = time_now_hires();

		TORRENT_ASSERT(completion_timeout > 0 || read_timeout > 0);

		if (m_abort) return;

		int timeout = 0;
		if (m_read_timeout > 0) timeout = m_read_timeout;
		if (m_completion_timeout > 0)
		{
			timeout = timeout == 0
				? m_completion_timeout
				: (std::min)(m_completion_timeout, timeout);
		}

#if defined TORRENT_ASIO_DEBUGGING
		add_outstanding_async("timeout_handler::timeout_callback");
#endif
		error_code ec;
		m_timeout.expires_at(m_read_time + seconds(timeout), ec);
		m_timeout.async_wait(boost::bind(
			&timeout_handler::timeout_callback, self(), _1));
	}

	void timeout_handler::restart_read_timeout()
	{
		m_read_time = time_now_hires();
	}

	void timeout_handler::cancel()
	{
		m_abort = true;
		m_completion_timeout = 0;
		error_code ec;
		m_timeout.cancel(ec);
	}

	void timeout_handler::timeout_callback(error_code const& error)
	{
#if defined TORRENT_ASIO_DEBUGGING
		complete_async("timeout_handler::timeout_callback");
#endif
		if (m_abort) return;

		ptime now = time_now_hires();
		time_duration receive_timeout = now - m_read_time;
		time_duration completion_timeout = now - m_start_time;

		if ((m_read_timeout
				&& m_read_timeout <= total_seconds(receive_timeout))
			|| (m_completion_timeout
				&& m_completion_timeout <= total_seconds(completion_timeout))
			|| error)
		{
			on_timeout(error);
			return;
		}

		int timeout = 0;
		if (m_read_timeout > 0) timeout = m_read_timeout;
		if (m_completion_timeout > 0)
		{
			timeout = timeout == 0
				? m_completion_timeout - total_seconds(m_read_time - m_start_time)
				: (std::min)(m_completion_timeout  - total_seconds(m_read_time - m_start_time), timeout);
		}
#if defined TORRENT_ASIO_DEBUGGING
		add_outstanding_async("timeout_handler::timeout_callback");
#endif
		error_code ec;
		m_timeout.expires_at(m_read_time + seconds(timeout), ec);
		m_timeout.async_wait(
			boost::bind(&timeout_handler::timeout_callback, self(), _1));
	}

	tracker_connection::tracker_connection(
		tracker_manager& man
		, tracker_request const& req
		, io_service& ios
		, boost::weak_ptr<request_callback> r)
		: timeout_handler(ios)
		, m_requester(r)
		, m_man(man)
		, m_req(req)
	{}

	boost::shared_ptr<request_callback> tracker_connection::requester() const
	{
		return m_requester.lock();
	}

	void tracker_connection::fail(error_code const& ec, int code
		, char const* msg, int interval, int min_interval)
	{
		boost::shared_ptr<request_callback> cb = requester();
		if (cb) cb->tracker_request_error(m_req, code, ec, msg
			, interval == 0 ? min_interval : interval);
		close();
	}

	void tracker_connection::sent_bytes(int bytes)
	{
		m_man.sent_bytes(bytes);
	}

	void tracker_connection::received_bytes(int bytes)
	{
		m_man.received_bytes(bytes);
	}

	void tracker_connection::close()
	{
		cancel();
		m_man.remove_request(this);
	}

	tracker_manager::~tracker_manager()
	{
		TORRENT_ASSERT(m_abort);
		abort_all_requests(true);
	}

	void tracker_manager::sent_bytes(int bytes)
	{
		TORRENT_ASSERT(m_ses.is_network_thread());
		m_ses.m_stat.sent_tracker_bytes(bytes);
	}

	void tracker_manager::received_bytes(int bytes)
	{
		TORRENT_ASSERT(m_ses.is_network_thread());
		m_ses.m_stat.received_tracker_bytes(bytes);
	}

	void tracker_manager::remove_request(tracker_connection const* c)
	{
		mutex_t::scoped_lock l(m_mutex);

		tracker_connections_t::iterator i = std::find(m_connections.begin()
			, m_connections.end(), boost::intrusive_ptr<const tracker_connection>(c));
		if (i == m_connections.end()) return;

		m_connections.erase(i);
	}

	void tracker_manager::queue_request(
		io_service& ios
		, connection_queue& cc
		, tracker_request req
		, std::string const& auth
		, boost::weak_ptr<request_callback> c)
	{
		mutex_t::scoped_lock l(m_mutex);
		TORRENT_ASSERT(req.num_want >= 0);
		TORRENT_ASSERT(!m_abort || req.event == tracker_request::stopped);
		if (m_abort && req.event != tracker_request::stopped) return;
		if (req.event == tracker_request::stopped)
			req.num_want = 0;

		TORRENT_ASSERT(!m_abort || req.event == tracker_request::stopped);
		if (m_abort && req.event != tracker_request::stopped)
			return;

		std::string protocol = req.url.substr(0, req.url.find(':'));

		boost::intrusive_ptr<tracker_connection> con;

#ifdef TORRENT_USE_OPENSSL
		if (protocol == "http" || protocol == "https")
#else
		if (protocol == "http")
#endif
		{
			con = new http_tracker_connection(
				ios, cc, *this, req, c
				, m_ses, m_proxy, auth
#if TORRENT_USE_I2P
				, &m_ses.m_i2p_conn
#endif
				);
		}
		else if (protocol == "udp")
		{
			con = new udp_tracker_connection(
				ios, cc, *this, req , c, m_ses
				, m_proxy);
		}
		else
		{
			// we need to post the error to avoid deadlock
			if (boost::shared_ptr<request_callback> r = c.lock())
				ios.post(boost::bind(&request_callback::tracker_request_error, r, req
					, -1, error_code(errors::unsupported_url_protocol)
					, "", 0));
			return;
		}

		m_connections.push_back(con);

		boost::shared_ptr<request_callback> cb = con->requester();
		if (cb) cb->m_manager = this;
		con->start();
	}

	bool tracker_manager::incoming_packet(error_code const& e
		, udp::endpoint const& ep, char const* buf, int size)
	{
		// m_ses.m_stat.received_tracker_bytes(len + 28);
		for (tracker_connections_t::iterator i = m_connections.begin();
			i != m_connections.end();)
		{
			boost::intrusive_ptr<tracker_connection> p = *i;
			++i;
			// on_receive() may remove the tracker connection from the list
			if (p->on_receive(e, ep, buf, size)) return true;
		}
		return false;
	}

	bool tracker_manager::incoming_packet(error_code const& e
		, char const* hostname, char const* buf, int size)
	{
		// m_ses.m_stat.received_tracker_bytes(len + 28);
		for (tracker_connections_t::iterator i = m_connections.begin();
			i != m_connections.end();)
		{
			boost::intrusive_ptr<tracker_connection> p = *i;
			++i;
			// on_receive() may remove the tracker connection from the list
			if (p->on_receive_hostname(e, hostname, buf, size)) return true;
		}
		return false;
	}

	void tracker_manager::abort_all_requests(bool all)
	{
		// removes all connections from m_connections
		// except 'event=stopped'-requests
		mutex_t::scoped_lock l(m_mutex);

		m_abort = true;
		tracker_connections_t close_connections;

		for (tracker_connections_t::iterator i = m_connections.begin()
			, end(m_connections.end()); i != end; ++i)
		{
			intrusive_ptr<tracker_connection> c = *i;
			tracker_request const& req = c->tracker_req();
			if (req.event == tracker_request::stopped && !all)
				continue;

			close_connections.push_back(c);

#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
			boost::shared_ptr<request_callback> rc = c->requester();
			if (rc) rc->debug_log("aborting: %s", req.url.c_str());
#endif
		}
		l.unlock();

		for (tracker_connections_t::iterator i = close_connections.begin()
			, end(close_connections.end()); i != end; ++i)
		{
			(*i)->close();
		}
	}

	bool tracker_manager::empty() const
	{
		mutex_t::scoped_lock l(m_mutex);
		return m_connections.empty();
	}

	int tracker_manager::num_requests() const
	{
		mutex_t::scoped_lock l(m_mutex);
		return m_connections.size();
	}
}
<commit_msg>fix ambiguity invoking min<commit_after>/*

Copyright (c) 2003-2012, Arvid Norberg
All rights reserved.

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 author 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.

*/

#include "libtorrent/pch.hpp"

#include <vector>
#include <cctype>

#include <boost/bind.hpp>

#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/http_tracker_connection.hpp"
#include "libtorrent/udp_tracker_connection.hpp"
#include "libtorrent/aux_/session_impl.hpp"

using boost::tuples::make_tuple;
using boost::tuples::tuple;

namespace
{
	enum
	{
		minimum_tracker_response_length = 3,
		http_buffer_size = 2048
	};

}

namespace libtorrent
{
	timeout_handler::timeout_handler(io_service& ios)
		: m_start_time(time_now_hires())
		, m_read_time(m_start_time)
		, m_timeout(ios)
		, m_completion_timeout(0)
		, m_read_timeout(0)
		, m_abort(false)
	{}

	void timeout_handler::set_timeout(int completion_timeout, int read_timeout)
	{
		m_completion_timeout = completion_timeout;
		m_read_timeout = read_timeout;
		m_start_time = m_read_time = time_now_hires();

		TORRENT_ASSERT(completion_timeout > 0 || read_timeout > 0);

		if (m_abort) return;

		int timeout = 0;
		if (m_read_timeout > 0) timeout = m_read_timeout;
		if (m_completion_timeout > 0)
		{
			timeout = timeout == 0
				? m_completion_timeout
				: (std::min)(m_completion_timeout, timeout);
		}

#if defined TORRENT_ASIO_DEBUGGING
		add_outstanding_async("timeout_handler::timeout_callback");
#endif
		error_code ec;
		m_timeout.expires_at(m_read_time + seconds(timeout), ec);
		m_timeout.async_wait(boost::bind(
			&timeout_handler::timeout_callback, self(), _1));
	}

	void timeout_handler::restart_read_timeout()
	{
		m_read_time = time_now_hires();
	}

	void timeout_handler::cancel()
	{
		m_abort = true;
		m_completion_timeout = 0;
		error_code ec;
		m_timeout.cancel(ec);
	}

	void timeout_handler::timeout_callback(error_code const& error)
	{
#if defined TORRENT_ASIO_DEBUGGING
		complete_async("timeout_handler::timeout_callback");
#endif
		if (m_abort) return;

		ptime now = time_now_hires();
		time_duration receive_timeout = now - m_read_time;
		time_duration completion_timeout = now - m_start_time;

		if ((m_read_timeout
				&& m_read_timeout <= total_seconds(receive_timeout))
			|| (m_completion_timeout
				&& m_completion_timeout <= total_seconds(completion_timeout))
			|| error)
		{
			on_timeout(error);
			return;
		}

		int timeout = 0;
		if (m_read_timeout > 0) timeout = m_read_timeout;
		if (m_completion_timeout > 0)
		{
			timeout = timeout == 0
				? m_completion_timeout - total_seconds(m_read_time - m_start_time)
				: (std::min)(int(m_completion_timeout - total_seconds(m_read_time - m_start_time)), timeout);
		}
#if defined TORRENT_ASIO_DEBUGGING
		add_outstanding_async("timeout_handler::timeout_callback");
#endif
		error_code ec;
		m_timeout.expires_at(m_read_time + seconds(timeout), ec);
		m_timeout.async_wait(
			boost::bind(&timeout_handler::timeout_callback, self(), _1));
	}

	tracker_connection::tracker_connection(
		tracker_manager& man
		, tracker_request const& req
		, io_service& ios
		, boost::weak_ptr<request_callback> r)
		: timeout_handler(ios)
		, m_requester(r)
		, m_man(man)
		, m_req(req)
	{}

	boost::shared_ptr<request_callback> tracker_connection::requester() const
	{
		return m_requester.lock();
	}

	void tracker_connection::fail(error_code const& ec, int code
		, char const* msg, int interval, int min_interval)
	{
		boost::shared_ptr<request_callback> cb = requester();
		if (cb) cb->tracker_request_error(m_req, code, ec, msg
			, interval == 0 ? min_interval : interval);
		close();
	}

	void tracker_connection::sent_bytes(int bytes)
	{
		m_man.sent_bytes(bytes);
	}

	void tracker_connection::received_bytes(int bytes)
	{
		m_man.received_bytes(bytes);
	}

	void tracker_connection::close()
	{
		cancel();
		m_man.remove_request(this);
	}

	tracker_manager::~tracker_manager()
	{
		TORRENT_ASSERT(m_abort);
		abort_all_requests(true);
	}

	void tracker_manager::sent_bytes(int bytes)
	{
		TORRENT_ASSERT(m_ses.is_network_thread());
		m_ses.m_stat.sent_tracker_bytes(bytes);
	}

	void tracker_manager::received_bytes(int bytes)
	{
		TORRENT_ASSERT(m_ses.is_network_thread());
		m_ses.m_stat.received_tracker_bytes(bytes);
	}

	void tracker_manager::remove_request(tracker_connection const* c)
	{
		mutex_t::scoped_lock l(m_mutex);

		tracker_connections_t::iterator i = std::find(m_connections.begin()
			, m_connections.end(), boost::intrusive_ptr<const tracker_connection>(c));
		if (i == m_connections.end()) return;

		m_connections.erase(i);
	}

	void tracker_manager::queue_request(
		io_service& ios
		, connection_queue& cc
		, tracker_request req
		, std::string const& auth
		, boost::weak_ptr<request_callback> c)
	{
		mutex_t::scoped_lock l(m_mutex);
		TORRENT_ASSERT(req.num_want >= 0);
		TORRENT_ASSERT(!m_abort || req.event == tracker_request::stopped);
		if (m_abort && req.event != tracker_request::stopped) return;
		if (req.event == tracker_request::stopped)
			req.num_want = 0;

		TORRENT_ASSERT(!m_abort || req.event == tracker_request::stopped);
		if (m_abort && req.event != tracker_request::stopped)
			return;

		std::string protocol = req.url.substr(0, req.url.find(':'));

		boost::intrusive_ptr<tracker_connection> con;

#ifdef TORRENT_USE_OPENSSL
		if (protocol == "http" || protocol == "https")
#else
		if (protocol == "http")
#endif
		{
			con = new http_tracker_connection(
				ios, cc, *this, req, c
				, m_ses, m_proxy, auth
#if TORRENT_USE_I2P
				, &m_ses.m_i2p_conn
#endif
				);
		}
		else if (protocol == "udp")
		{
			con = new udp_tracker_connection(
				ios, cc, *this, req , c, m_ses
				, m_proxy);
		}
		else
		{
			// we need to post the error to avoid deadlock
			if (boost::shared_ptr<request_callback> r = c.lock())
				ios.post(boost::bind(&request_callback::tracker_request_error, r, req
					, -1, error_code(errors::unsupported_url_protocol)
					, "", 0));
			return;
		}

		m_connections.push_back(con);

		boost::shared_ptr<request_callback> cb = con->requester();
		if (cb) cb->m_manager = this;
		con->start();
	}

	bool tracker_manager::incoming_packet(error_code const& e
		, udp::endpoint const& ep, char const* buf, int size)
	{
		// m_ses.m_stat.received_tracker_bytes(len + 28);
		for (tracker_connections_t::iterator i = m_connections.begin();
			i != m_connections.end();)
		{
			boost::intrusive_ptr<tracker_connection> p = *i;
			++i;
			// on_receive() may remove the tracker connection from the list
			if (p->on_receive(e, ep, buf, size)) return true;
		}
		return false;
	}

	bool tracker_manager::incoming_packet(error_code const& e
		, char const* hostname, char const* buf, int size)
	{
		// m_ses.m_stat.received_tracker_bytes(len + 28);
		for (tracker_connections_t::iterator i = m_connections.begin();
			i != m_connections.end();)
		{
			boost::intrusive_ptr<tracker_connection> p = *i;
			++i;
			// on_receive() may remove the tracker connection from the list
			if (p->on_receive_hostname(e, hostname, buf, size)) return true;
		}
		return false;
	}

	void tracker_manager::abort_all_requests(bool all)
	{
		// removes all connections from m_connections
		// except 'event=stopped'-requests
		mutex_t::scoped_lock l(m_mutex);

		m_abort = true;
		tracker_connections_t close_connections;

		for (tracker_connections_t::iterator i = m_connections.begin()
			, end(m_connections.end()); i != end; ++i)
		{
			intrusive_ptr<tracker_connection> c = *i;
			tracker_request const& req = c->tracker_req();
			if (req.event == tracker_request::stopped && !all)
				continue;

			close_connections.push_back(c);

#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
			boost::shared_ptr<request_callback> rc = c->requester();
			if (rc) rc->debug_log("aborting: %s", req.url.c_str());
#endif
		}
		l.unlock();

		for (tracker_connections_t::iterator i = close_connections.begin()
			, end(close_connections.end()); i != end; ++i)
		{
			(*i)->close();
		}
	}

	bool tracker_manager::empty() const
	{
		mutex_t::scoped_lock l(m_mutex);
		return m_connections.empty();
	}

	int tracker_manager::num_requests() const
	{
		mutex_t::scoped_lock l(m_mutex);
		return m_connections.size();
	}
}
<|endoftext|>


<commit_before>/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.

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.
==============================================================================*/
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>

#include "tensorflow/contrib/lite/toco/graph_transformations/graph_transformations.h"
#include "tensorflow/contrib/lite/toco/model.h"
#include "tensorflow/core/platform/logging.h"

namespace toco {

namespace {

ArrayDataType CommonDataTypeOfAllInputs(const Model& model,
                                        const Operator& op) {
  CHECK_GT(op.inputs.size(), 0);
  const ArrayDataType data_type = model.GetArray(op.inputs[0]).data_type;
  for (const auto& input : op.inputs) {
    const auto& array = model.GetArray(input);
    CHECK(array.data_type == data_type)
        << " Unexpected: this operator has inputs with different data types.";
  }
  return data_type;
}

void SetDataTypeForAllOutputs(Model* model, Operator* op,
                              ArrayDataType data_type) {
  for (const auto& output : op->outputs) {
    model->arrays[output]->data_type = data_type;
  }
}
}  // namespace

bool PropagateArrayDataTypes::Run(Model* model, std::size_t op_index) {
  auto it = model->operators.begin() + op_index;
  auto* op = it->get();

  // If the data type of some input is unknown, we need to yield.
  for (const auto& input : op->inputs) {
    if (model->arrays[input]->data_type == ArrayDataType::kNone) {
      return false;
    }
  }
  // Record data types of output before processing, so we can see at the
  // end if we changed anything, and return the correct boolean value.
  std::unordered_map<string, ArrayDataType> old_output_data_types;
  for (const auto& output : op->outputs) {
    old_output_data_types[output] = model->arrays[output]->data_type;
  }
  // Do the actual output data types propagation.
  if (op->type == OperatorType::kDequantize ||
      op->type == OperatorType::kResizeBilinear) {
    // These operators unconditionally produce float outputs
    SetDataTypeForAllOutputs(model, op, ArrayDataType::kFloat);
  } else if (op->type == OperatorType::kTensorFlowLess ||
             op->type == OperatorType::kTensorFlowLessEqual ||
             op->type == OperatorType::kTensorFlowGreater ||
             op->type == OperatorType::kTensorFlowGreaterEqual) {
    // These operators unconditionally produce bool outputs
    SetDataTypeForAllOutputs(model, op, ArrayDataType::kBool);
  } else if (op->type == OperatorType::kTensorFlowShape) {
    // These operators are assumed to produce int32 outputs.
    SetDataTypeForAllOutputs(model, op, ArrayDataType::kInt32);
  } else if (op->type == OperatorType::kAveragePool ||
             op->type == OperatorType::kMaxPool ||
             op->type == OperatorType::kL2Pool ||
             op->type == OperatorType::kConv ||
             op->type == OperatorType::kDepthwiseConv ||
             op->type == OperatorType::kFullyConnected ||
             op->type == OperatorType::kTensorFlowMax ||
             op->type == OperatorType::kTensorFlowMin ||
             op->type == OperatorType::kPad ||
             op->type == OperatorType::kStridedSlice ||
             op->type == OperatorType::kTensorFlowReshape ||
             op->type == OperatorType::kSlice ||
             op->type == OperatorType::kSqueeze ||
             op->type == OperatorType::kTensorFlowSum ||
             op->type == OperatorType::kTensorFlowSwitch ||
             op->type == OperatorType::kTensorFlowTile ||
             op->type == OperatorType::kTensorFlowAll ||
             op->type == OperatorType::kReorderAxes ||
             op->type == OperatorType::kTensorFlowConcatV2 ||
             op->type == OperatorType::kFloor ||
             op->type == OperatorType::kGather ||
             op->type == OperatorType::kSpaceToBatchND ||
             op->type == OperatorType::kBatchToSpaceND ||
             op->type == OperatorType::kMean) {
    // These operators produce outputs with the same type as their 1st input
    CHECK_GT(op->inputs.size(), 0);
    const ArrayDataType data_type = model->arrays[op->inputs[0]]->data_type;
    SetDataTypeForAllOutputs(model, op, data_type);
  } else if (op->type == OperatorType::kTensorFlowSplit ||
             op->type == OperatorType::kTensorFlowConcat) {
    // These operators produce an output with the same type as their 2nd input
    CHECK_GT(op->inputs.size(), 1);
    const ArrayDataType data_type = model->arrays[op->inputs[1]]->data_type;
    SetDataTypeForAllOutputs(model, op, data_type);
  } else if (op->type == OperatorType::kCast) {
    // Data type of the Cast op is specified.
    CHECK_EQ(op->outputs.size(), 1);
    auto* cast_op = static_cast<CastOperator*>(op);
    model->arrays[op->outputs[0]]->data_type = cast_op->dst_data_type;
  } else if (op->type == OperatorType::kTensorFlowUnsupported) {
    auto* unsupported_op = static_cast<TensorFlowUnsupportedOperator*>(op);
    if (unsupported_op->output_data_types.size() != op->outputs.size()) {
      return false;
    }
    for (int i = 0; i < unsupported_op->output_data_types.size(); ++i) {
      auto output = op->outputs[i];
      auto data_type = unsupported_op->output_data_types[i];
      model->arrays[output]->data_type = data_type;
    }
  } else {
    // These operators produce an output with the same type as any of their
    // inputs, which must always have the same type.
    const ArrayDataType data_type = CommonDataTypeOfAllInputs(*model, *op);
    SetDataTypeForAllOutputs(model, op, data_type);
  }
  // Return true if any output data type changed, false if none changed.
  for (const auto& output : op->outputs) {
    if (old_output_data_types[output] != model->arrays[output]->data_type) {
      return true;
    }
  }
  return false;
}

}  // namespace toco
<commit_msg>Simplification of propagate_array_data_types: No need to distinguish between ops for which we propagate the type of the first input, and ops for which we require all inputs to have the same type then propagate that type. Doing only the former is a mild relaxation of what we've been doing, allows to drop much code, and makes this code far less frequently needing to be updated again in the future as we drop a long non-canonical list of operator types being special-cased.<commit_after>/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.

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.
==============================================================================*/
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>

#include "tensorflow/contrib/lite/toco/graph_transformations/graph_transformations.h"
#include "tensorflow/contrib/lite/toco/model.h"
#include "tensorflow/core/platform/logging.h"

namespace toco {

namespace {
void SetDataTypeForAllOutputs(Model* model, Operator* op,
                              ArrayDataType data_type) {
  for (const auto& output : op->outputs) {
    model->arrays[output]->data_type = data_type;
  }
}
}  // namespace

bool PropagateArrayDataTypes::Run(Model* model, std::size_t op_index) {
  auto it = model->operators.begin() + op_index;
  auto* op = it->get();

  // If the data type of some input is unknown, we need to yield.
  for (const auto& input : op->inputs) {
    if (model->arrays[input]->data_type == ArrayDataType::kNone) {
      return false;
    }
  }
  // Record data types of output before processing, so we can see at the
  // end if we changed anything, and return the correct boolean value.
  std::unordered_map<string, ArrayDataType> old_output_data_types;
  for (const auto& output : op->outputs) {
    old_output_data_types[output] = model->arrays[output]->data_type;
  }
  // Do the actual output data types propagation.
  if (op->type == OperatorType::kDequantize ||
      op->type == OperatorType::kResizeBilinear) {
    // These operators unconditionally produce float outputs
    SetDataTypeForAllOutputs(model, op, ArrayDataType::kFloat);
  } else if (op->type == OperatorType::kTensorFlowLess ||
             op->type == OperatorType::kTensorFlowLessEqual ||
             op->type == OperatorType::kTensorFlowGreater ||
             op->type == OperatorType::kTensorFlowGreaterEqual) {
    // These operators unconditionally produce bool outputs
    SetDataTypeForAllOutputs(model, op, ArrayDataType::kBool);
  } else if (op->type == OperatorType::kTensorFlowShape) {
    // These operators are assumed to produce int32 outputs.
    SetDataTypeForAllOutputs(model, op, ArrayDataType::kInt32);
  } else if (op->type == OperatorType::kTensorFlowSplit ||
             op->type == OperatorType::kTensorFlowConcat) {
    // These operators produce an output with the same type as their 2nd input
    CHECK_GT(op->inputs.size(), 1);
    const ArrayDataType data_type = model->arrays[op->inputs[1]]->data_type;
    SetDataTypeForAllOutputs(model, op, data_type);
  } else if (op->type == OperatorType::kCast) {
    // Data type of the Cast op is specified.
    CHECK_EQ(op->outputs.size(), 1);
    auto* cast_op = static_cast<CastOperator*>(op);
    model->arrays[op->outputs[0]]->data_type = cast_op->dst_data_type;
  } else if (op->type == OperatorType::kTensorFlowUnsupported) {
    auto* unsupported_op = static_cast<TensorFlowUnsupportedOperator*>(op);
    if (unsupported_op->output_data_types.size() != op->outputs.size()) {
      return false;
    }
    for (int i = 0; i < unsupported_op->output_data_types.size(); ++i) {
      auto output = op->outputs[i];
      auto data_type = unsupported_op->output_data_types[i];
      model->arrays[output]->data_type = data_type;
    }
  } else {
    // These operators produce outputs with the same type as their 1st input
    CHECK_GT(op->inputs.size(), 0);
    const ArrayDataType data_type = model->arrays[op->inputs[0]]->data_type;
    SetDataTypeForAllOutputs(model, op, data_type);
  }
  // Return true if any output data type changed, false if none changed.
  for (const auto& output : op->outputs) {
    if (old_output_data_types[output] != model->arrays[output]->data_type) {
      return true;
    }
  }
  return false;
}

}  // namespace toco
<|endoftext|>


<commit_before>//* This file is part of the MOOSE framework
//* https://www.mooseframework.org
//*
//* All rights reserved, see COPYRIGHT for full restrictions
//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
//*
//* Licensed under LGPL 2.1, please see LICENSE for details
//* https://www.gnu.org/licenses/lgpl-2.1.html

#include "AbaqusUMATStress.h"
#include "Factory.h"
#include "MooseMesh.h"
#include "RankTwoTensor.h"
#include "RankFourTensor.h"
#include "libmesh/int_range.h"
#include <string.h>
#include <algorithm>

#define QUOTE(macro) stringifyName(macro)

MooseEnum
AbaqusUMATStress::decompositionType()
{
  return MooseEnum("TaylorExpansion EigenSolution HughesWinget", "TaylorExpansion");
}

registerMooseObject("TensorMechanicsApp", AbaqusUMATStress);

InputParameters
AbaqusUMATStress::validParams()
{
  InputParameters params = ComputeStressBase::validParams();
  params.addClassDescription("Coupling material to use Abaqus UMAT models in MOOSE");
  params.addRequiredParam<FileName>(
      "plugin", "The path to the compiled dynamic library for the plugin you want to use");
  params.addRequiredParam<bool>(
      "use_one_based_indexing",
      "Parameter to control whether indexing for element and integration points as presented to "
      "UMAT models is based on 1 (true) or 0 (false). This does not affect internal MOOSE "
      "numbering. The option to use 0-based numbering is deprecated and will be removed soon.");
  params.addRequiredParam<std::vector<Real>>(
      "constant_properties", "Constant mechanical and thermal material properties (PROPS)");
  params.addRequiredParam<unsigned int>("num_state_vars",
                                        "The number of state variables this UMAT is going to use");
  params.addCoupledVar("temperature", 0.0, "Coupled temperature");

  params.addCoupledVar("external_fields",
                       "The external fields that can be used in the UMAT subroutine");
  params.addParam<std::vector<MaterialPropertyName>>("external_properties", "");
  params.addParam<MooseEnum>("decomposition_method",
			     AbaqusUMATStress::decompositionType(),
                             "Method to calcualte the strain kinematics.");
  return params;
}

#ifndef METHOD
#error "The METHOD preprocessor symbol must be supplied by the build system."
#endif

AbaqusUMATStress::AbaqusUMATStress(const InputParameters & parameters)
  : ComputeStressBase(parameters),
    _plugin(getParam<FileName>("plugin")),
    _library(_plugin + std::string("-") + QUOTE(METHOD) + ".plugin"),
    _umat(_library.getFunction<umat_t>("umat_")),
    _aqNSTATV(getParam<unsigned int>("num_state_vars")),
    _aqSTATEV(_aqNSTATV),
    _aqPROPS(getParam<std::vector<Real>>("constant_properties")),
    _aqNPROPS(_aqPROPS.size()),
    _stress_old(getMaterialPropertyOld<RankTwoTensor>(_base_name + "stress")),
    _total_strain_old(getMaterialPropertyOld<RankTwoTensor>(_base_name + "total_strain")),
    _strain_increment(getOptionalMaterialProperty<RankTwoTensor>(_base_name + "strain_increment")),
    _jacobian_mult(declareProperty<RankFourTensor>(_base_name + "Jacobian_mult")),
    _Fbar(getOptionalMaterialProperty<RankTwoTensor>(_base_name + "deformation_gradient")),
    _Fbar_old(getOptionalMaterialPropertyOld<RankTwoTensor>(_base_name + "deformation_gradient")),
    _state_var(declareProperty<std::vector<Real>>(_base_name + "state_var")),
    _state_var_old(getMaterialPropertyOld<std::vector<Real>>(_base_name + "state_var")),
    _elastic_strain_energy(declareProperty<Real>(_base_name + "elastic_strain_energy")),
    _plastic_dissipation(declareProperty<Real>(_base_name + "plastic_dissipation")),
    _creep_dissipation(declareProperty<Real>(_base_name + "creep_dissipation")),
    _material_timestep(declareProperty<Real>(_base_name + "material_timestep_limit")),
    _rotation_increment(
        getOptionalMaterialProperty<RankTwoTensor>(_base_name + "rotation_increment")),
    _temperature(coupledValue("temperature")),
    _temperature_old(coupledValueOld("temperature")),
    _external_fields(coupledValues("external_fields")),
    _external_fields_old(coupledValuesOld("external_fields")),
    _number_external_fields(_external_fields.size()),
    _external_property_names(getParam<std::vector<MaterialPropertyName>>("external_properties")),
    _number_external_properties(_external_property_names.size()),
    _external_properties(_number_external_properties),
    _external_properties_old(_number_external_properties),
    _use_one_based_indexing(getParam<bool>("use_one_based_indexing")),
    _decomposition_method(getParam<MooseEnum>("decomposition_method").getEnum<DecompMethod>())
{
  if (!_use_one_based_indexing)
    mooseDeprecated(
        "AbaqusUMATStress has transitioned to 1-based indexing in the element (NOEL) and "
        "integration point (NPT) numbers to ensure maximum compatibility with legacy UMAT files. "
        "Please ensure that any new UMAT plugins using these quantities are using the correct "
        "indexing. 0-based indexing will be deprecated soon.");

  // get material properties
  for (std::size_t i = 0; i < _number_external_properties; ++i)
  {
    _external_properties[i] = &getMaterialProperty<Real>(_external_property_names[i]);
    _external_properties_old[i] = &getMaterialPropertyOld<Real>(_external_property_names[i]);
  }

  // Read mesh dimension and size UMAT arrays (we always size for full 3D)
  _aqNTENS = 6; // Size of the stress or strain component array (NDI+NSHR)
  _aqNSHR = 3;  // Number of engineering shear stress components
  _aqNDI = 3;   // Number of direct stress components (always 3)

  _aqDDSDDT.resize(_aqNTENS);
  _aqDRPLDE.resize(_aqNTENS);
  _aqSTRAN.resize(_aqNTENS);
  _aqDFGRD0.resize(9);
  _aqDFGRD1.resize(9);
  _aqDROT.resize(9);
  _aqSTRESS.resize(_aqNTENS);
  _aqDDSDDE.resize(_aqNTENS * _aqNTENS);
  _aqDSTRAN.resize(_aqNTENS);
  _aqPREDEF.resize(_number_external_fields + _number_external_properties);
  _aqDPRED.resize(_number_external_fields + _number_external_properties);
}

void
AbaqusUMATStress::initialSetup()
{
  // The _Fbar, _Fbar_old, and _rotation_increment optional properties are only available when an
  // incremental strain formulation is used. If they are not avaliable we advide the user to
  // select an incremental formulation.
  if (!_strain_increment || !_Fbar || !_Fbar_old || !_rotation_increment)
    mooseError("AbaqusUMATStress '",
               name(),
               "': Incremental strain quantities are not available. You likely are using a total "
               "strain formulation. Specify `incremental = true` in the tensor mechanics action, "
               "or use ComputeIncrementalSmallStrain in your input file.");
}

void
AbaqusUMATStress::initQpStatefulProperties()
{
  ComputeStressBase::initQpStatefulProperties();

  // Initialize state variable vector
  _state_var[_qp].resize(_aqNSTATV);
  for (const auto i : make_range(_aqNSTATV))
    _state_var[_qp][i] = 0.0;
}

void
AbaqusUMATStress::computeProperties()
{
  // current element "number"
  _aqNOEL = _current_elem->id() + (_use_one_based_indexing ? 1 : 0);

  // characteristic element length
  _aqCELENT = std::pow(_current_elem->volume(), 1.0 / _current_elem->dim());

  // For now, total time and step time mean the exact same thing
  // Value of step time at the beginning of the current increment
  _aqTIME[0] = _t - _dt;
  // Value of total time at the beginning of the current increment
  _aqTIME[1] = _t - _dt;

  // Time increment
  _aqDTIME = _dt;

  // Fill unused characters with spaces (Fortran)
  std::fill(_aqCMNAME, _aqCMNAME + 80, ' ');
  std::memcpy(_aqCMNAME, name().c_str(), name().size());

  ComputeStressBase::computeProperties();
}

void
AbaqusUMATStress::computeQpStress()
{
  const Real * myDFGRD0 = &(_Fbar_old[_qp](0, 0));
  const Real * myDFGRD1 = &(_Fbar[_qp](0, 0));
  const Real * myDROT = &(_rotation_increment[_qp](0, 0));

  // copy because UMAT does not guarantee constness
  for (const auto i : make_range(9))
  {
    _aqDFGRD0[i] = myDFGRD0[i];
    _aqDFGRD1[i] = myDFGRD1[i];
    _aqDROT[i] = myDROT[i];
  }

  // Recover "old" state variables
  for (const auto i : make_range(_aqNSTATV))
    _aqSTATEV[i] = _state_var_old[_qp][i];

  // Pass through updated stress, total strain, and strain increment arrays
  static const std::array<Real, 6> strain_factor{{1, 1, 1, 2, 2, 2}};
  // Account for difference in vector order convention: yz, xz, xy (MOOSE)  vs xy, xz, yz
  // (commercial software)
  static const std::array<std::pair<unsigned int, unsigned int>, 6> component{
      {{0, 0}, {1, 1}, {2, 2}, {0, 1}, {0, 2}, {1, 2}}};

  // rotate old stress if HughesWinget
  RankTwoTensor stress_old = _stress_old[_qp];
  if (_decomposition_method == DecompMethod::HughesWinget)
    stress_old.rotate(_rotation_increment[_qp]);

  for (const auto i : make_range(_aqNTENS))
  {
    const auto a = component[i].first;
    const auto b = component[i].second;
    _aqSTRESS[i] = stress_old(a, b);
    _aqSTRAN[i] = _total_strain_old[_qp](a, b) * strain_factor[i];
    _aqDSTRAN[i] = _strain_increment[_qp](a, b) * strain_factor[i];
  }

  // current coordinates
  for (const auto i : make_range(Moose::dim))
    _aqCOORDS[i] = _q_point[_qp](i);

  // zero out Jacobian contribution
  for (const auto i : make_range(_aqNTENS * _aqNTENS))
    _aqDDSDDE[i] = 0.0;

  // Set PNEWDT initially to a large value
  _aqPNEWDT = std::numeric_limits<Real>::max();

  // Temperature
  _aqTEMP = _temperature_old[_qp];

  // Temperature increment
  _aqDTEMP = _temperature[_qp] - _temperature_old[_qp];

  for (const auto i : make_range(_number_external_fields))
  {
    // External field at this step
    _aqPREDEF[i] = (*_external_fields_old[i])[_qp];

    // External field increments
    _aqDPRED[i] = (*_external_fields[i])[_qp] - (*_external_fields_old[i])[_qp];
  }

  for (const auto i : make_range(_number_external_properties))
  {
    // External property at this step
    _aqPREDEF[i + _number_external_fields] = (*_external_properties_old[i])[_qp];

    // External property increments
    _aqDPRED[i + _number_external_fields] =
        (*_external_properties[i])[_qp] - (*_external_properties_old[i])[_qp];
  }

  // Layer number (not supported)
  _aqLAYER = -1;

  // Section point number within the layer (not supported)
  _aqKSPT = -1;

  // Increment number
  _aqKINC = _t_step;
  _aqKSTEP = 1;

  // integration point number
  _aqNPT = _qp + (_use_one_based_indexing ? 1 : 0);

  // Connection to extern statement
  _umat(_aqSTRESS.data(),
        _aqSTATEV.data(),
        _aqDDSDDE.data(),
        &_elastic_strain_energy[_qp],
        &_plastic_dissipation[_qp],
        &_creep_dissipation[_qp],
        &_aqRPL,
        _aqDDSDDT.data(),
        _aqDRPLDE.data(),
        &_aqDRPLDT,
        _aqSTRAN.data(),
        _aqDSTRAN.data(),
        _aqTIME.data(),
        &_aqDTIME,
        &_aqTEMP,
        &_aqDTEMP,
        _aqPREDEF.data(),
        _aqDPRED.data(),
        _aqCMNAME,
        &_aqNDI,
        &_aqNSHR,
        &_aqNTENS,
        &_aqNSTATV,
        _aqPROPS.data(),
        &_aqNPROPS,
        _aqCOORDS.data(),
        _aqDROT.data(),
        &_aqPNEWDT,
        &_aqCELENT,
        _aqDFGRD0.data(),
        _aqDFGRD1.data(),
        &_aqNOEL,
        &_aqNPT,
        &_aqLAYER,
        &_aqKSPT,
        &_aqKSTEP,
        &_aqKINC);

  // Update state variables
  for (int i = 0; i < _aqNSTATV; ++i)
    _state_var[_qp][i] = _aqSTATEV[i];

  // Here, we apply UMAT convention: Always multiply _dt by PNEWDT to determine the material time
  // step MOOSE time stepper will choose the most limiting of all material time step increments
  // provided
  _material_timestep[_qp] = _aqPNEWDT * _dt;

  // Get new stress tensor - UMAT should update stress
  // Account for difference in vector order convention: yz, xz, xy (MOOSE)  vs xy, xz, yz
  // (commercial software)
  _stress[_qp] = RankTwoTensor(
      _aqSTRESS[0], _aqSTRESS[1], _aqSTRESS[2], _aqSTRESS[5], _aqSTRESS[4], _aqSTRESS[3]);

  // Rotate the stress state to the current configuration, unless HughesWinget
  if (_decomposition_method != DecompMethod::HughesWinget)
    _stress[_qp].rotate(_rotation_increment[_qp]);

  // Build Jacobian matrix from UMAT's Voigt non-standard order to fourth order tensor.
  const unsigned int N = Moose::dim;
  const unsigned int ntens = N * (N + 1) / 2;
  const int nskip = N - 1;

  for (const auto i : make_range(N))
    for (const auto j : make_range(N))
      for (const auto k : make_range(N))
        for (const auto l : make_range(N))
        {
          if (i == j)
            _jacobian_mult[_qp](i, j, k, l) =
                k == l ? _aqDDSDDE[i * ntens + k] : _aqDDSDDE[i * ntens + k + nskip + l];
          else
            // i!=j
            _jacobian_mult[_qp](i, j, k, l) =
                k == l ? _aqDDSDDE[(nskip + i + j) * ntens + k]
                       : _aqDDSDDE[(nskip + i + j) * ntens + k + nskip + l];
        }
}
<commit_msg>Fixed column vs row major bug in UMAT interface<commit_after>//* This file is part of the MOOSE framework
//* https://www.mooseframework.org
//*
//* All rights reserved, see COPYRIGHT for full restrictions
//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
//*
//* Licensed under LGPL 2.1, please see LICENSE for details
//* https://www.gnu.org/licenses/lgpl-2.1.html

#include "AbaqusUMATStress.h"
#include "Factory.h"
#include "MooseMesh.h"
#include "RankTwoTensor.h"
#include "RankFourTensor.h"
#include "libmesh/int_range.h"
#include <string.h>
#include <algorithm>

#define QUOTE(macro) stringifyName(macro)

MooseEnum
AbaqusUMATStress::decompositionType()
{
  return MooseEnum("TaylorExpansion EigenSolution HughesWinget", "TaylorExpansion");
}

registerMooseObject("TensorMechanicsApp", AbaqusUMATStress);

InputParameters
AbaqusUMATStress::validParams()
{
  InputParameters params = ComputeStressBase::validParams();
  params.addClassDescription("Coupling material to use Abaqus UMAT models in MOOSE");
  params.addRequiredParam<FileName>(
      "plugin", "The path to the compiled dynamic library for the plugin you want to use");
  params.addRequiredParam<bool>(
      "use_one_based_indexing",
      "Parameter to control whether indexing for element and integration points as presented to "
      "UMAT models is based on 1 (true) or 0 (false). This does not affect internal MOOSE "
      "numbering. The option to use 0-based numbering is deprecated and will be removed soon.");
  params.addRequiredParam<std::vector<Real>>(
      "constant_properties", "Constant mechanical and thermal material properties (PROPS)");
  params.addRequiredParam<unsigned int>("num_state_vars",
                                        "The number of state variables this UMAT is going to use");
  params.addCoupledVar("temperature", 0.0, "Coupled temperature");

  params.addCoupledVar("external_fields",
                       "The external fields that can be used in the UMAT subroutine");
  params.addParam<std::vector<MaterialPropertyName>>("external_properties", "");
  params.addParam<MooseEnum>("decomposition_method",
			     AbaqusUMATStress::decompositionType(),
                             "Method to calcualte the strain kinematics.");
  return params;
}

#ifndef METHOD
#error "The METHOD preprocessor symbol must be supplied by the build system."
#endif

AbaqusUMATStress::AbaqusUMATStress(const InputParameters & parameters)
  : ComputeStressBase(parameters),
    _plugin(getParam<FileName>("plugin")),
    _library(_plugin + std::string("-") + QUOTE(METHOD) + ".plugin"),
    _umat(_library.getFunction<umat_t>("umat_")),
    _aqNSTATV(getParam<unsigned int>("num_state_vars")),
    _aqSTATEV(_aqNSTATV),
    _aqPROPS(getParam<std::vector<Real>>("constant_properties")),
    _aqNPROPS(_aqPROPS.size()),
    _stress_old(getMaterialPropertyOld<RankTwoTensor>(_base_name + "stress")),
    _total_strain_old(getMaterialPropertyOld<RankTwoTensor>(_base_name + "total_strain")),
    _strain_increment(getOptionalMaterialProperty<RankTwoTensor>(_base_name + "strain_increment")),
    _jacobian_mult(declareProperty<RankFourTensor>(_base_name + "Jacobian_mult")),
    _Fbar(getOptionalMaterialProperty<RankTwoTensor>(_base_name + "deformation_gradient")),
    _Fbar_old(getOptionalMaterialPropertyOld<RankTwoTensor>(_base_name + "deformation_gradient")),
    _state_var(declareProperty<std::vector<Real>>(_base_name + "state_var")),
    _state_var_old(getMaterialPropertyOld<std::vector<Real>>(_base_name + "state_var")),
    _elastic_strain_energy(declareProperty<Real>(_base_name + "elastic_strain_energy")),
    _plastic_dissipation(declareProperty<Real>(_base_name + "plastic_dissipation")),
    _creep_dissipation(declareProperty<Real>(_base_name + "creep_dissipation")),
    _material_timestep(declareProperty<Real>(_base_name + "material_timestep_limit")),
    _rotation_increment(
        getOptionalMaterialProperty<RankTwoTensor>(_base_name + "rotation_increment")),
    _temperature(coupledValue("temperature")),
    _temperature_old(coupledValueOld("temperature")),
    _external_fields(coupledValues("external_fields")),
    _external_fields_old(coupledValuesOld("external_fields")),
    _number_external_fields(_external_fields.size()),
    _external_property_names(getParam<std::vector<MaterialPropertyName>>("external_properties")),
    _number_external_properties(_external_property_names.size()),
    _external_properties(_number_external_properties),
    _external_properties_old(_number_external_properties),
    _use_one_based_indexing(getParam<bool>("use_one_based_indexing")),
    _decomposition_method(getParam<MooseEnum>("decomposition_method").getEnum<DecompMethod>())
{
  if (!_use_one_based_indexing)
    mooseDeprecated(
        "AbaqusUMATStress has transitioned to 1-based indexing in the element (NOEL) and "
        "integration point (NPT) numbers to ensure maximum compatibility with legacy UMAT files. "
        "Please ensure that any new UMAT plugins using these quantities are using the correct "
        "indexing. 0-based indexing will be deprecated soon.");

  // get material properties
  for (std::size_t i = 0; i < _number_external_properties; ++i)
  {
    _external_properties[i] = &getMaterialProperty<Real>(_external_property_names[i]);
    _external_properties_old[i] = &getMaterialPropertyOld<Real>(_external_property_names[i]);
  }

  // Read mesh dimension and size UMAT arrays (we always size for full 3D)
  _aqNTENS = 6; // Size of the stress or strain component array (NDI+NSHR)
  _aqNSHR = 3;  // Number of engineering shear stress components
  _aqNDI = 3;   // Number of direct stress components (always 3)

  _aqDDSDDT.resize(_aqNTENS);
  _aqDRPLDE.resize(_aqNTENS);
  _aqSTRAN.resize(_aqNTENS);
  _aqDFGRD0.resize(9);
  _aqDFGRD1.resize(9);
  _aqDROT.resize(9);
  _aqSTRESS.resize(_aqNTENS);
  _aqDDSDDE.resize(_aqNTENS * _aqNTENS);
  _aqDSTRAN.resize(_aqNTENS);
  _aqPREDEF.resize(_number_external_fields + _number_external_properties);
  _aqDPRED.resize(_number_external_fields + _number_external_properties);
}

void
AbaqusUMATStress::initialSetup()
{
  // The _Fbar, _Fbar_old, and _rotation_increment optional properties are only available when an
  // incremental strain formulation is used. If they are not avaliable we advide the user to
  // select an incremental formulation.
  if (!_strain_increment || !_Fbar || !_Fbar_old || !_rotation_increment)
    mooseError("AbaqusUMATStress '",
               name(),
               "': Incremental strain quantities are not available. You likely are using a total "
               "strain formulation. Specify `incremental = true` in the tensor mechanics action, "
               "or use ComputeIncrementalSmallStrain in your input file.");
}

void
AbaqusUMATStress::initQpStatefulProperties()
{
  ComputeStressBase::initQpStatefulProperties();

  // Initialize state variable vector
  _state_var[_qp].resize(_aqNSTATV);
  for (const auto i : make_range(_aqNSTATV))
    _state_var[_qp][i] = 0.0;
}

void
AbaqusUMATStress::computeProperties()
{
  // current element "number"
  _aqNOEL = _current_elem->id() + (_use_one_based_indexing ? 1 : 0);

  // characteristic element length
  _aqCELENT = std::pow(_current_elem->volume(), 1.0 / _current_elem->dim());

  // For now, total time and step time mean the exact same thing
  // Value of step time at the beginning of the current increment
  _aqTIME[0] = _t - _dt;
  // Value of total time at the beginning of the current increment
  _aqTIME[1] = _t - _dt;

  // Time increment
  _aqDTIME = _dt;

  // Fill unused characters with spaces (Fortran)
  std::fill(_aqCMNAME, _aqCMNAME + 80, ' ');
  std::memcpy(_aqCMNAME, name().c_str(), name().size());

  ComputeStressBase::computeProperties();
}

void
AbaqusUMATStress::computeQpStress()
{
  // C uses row-major, whereas Fortran uses column major
  // therefore, all unsymmetric matrices must be transposed before passing them to Fortran
  RankTwoTensor FBar_old_fortran = _Fbar_old[_qp].transpose();
  RankTwoTensor FBar_fortran = _Fbar[_qp].transpose();
  RankTwoTensor DROT_fortran = _rotation_increment[_qp].transpose();
  const Real * myDFGRD0 = &(FBar_old_fortran(0, 0));
  const Real * myDFGRD1 = &(FBar_fortran(0, 0));
  const Real * myDROT = &(DROT_fortran(0, 0));

  // copy because UMAT does not guarantee constness
  for (const auto i : make_range(9))
  {
    _aqDFGRD0[i] = myDFGRD0[i];
    _aqDFGRD1[i] = myDFGRD1[i];
    _aqDROT[i] = myDROT[i];
  }

  // Recover "old" state variables
  for (const auto i : make_range(_aqNSTATV))
    _aqSTATEV[i] = _state_var_old[_qp][i];

  // Pass through updated stress, total strain, and strain increment arrays
  static const std::array<Real, 6> strain_factor{{1, 1, 1, 2, 2, 2}};
  // Account for difference in vector order convention: yz, xz, xy (MOOSE)  vs xy, xz, yz
  // (commercial software)
  static const std::array<std::pair<unsigned int, unsigned int>, 6> component{
      {{0, 0}, {1, 1}, {2, 2}, {0, 1}, {0, 2}, {1, 2}}};

  // rotate old stress if HughesWinget
  RankTwoTensor stress_old = _stress_old[_qp];
  if (_decomposition_method == DecompMethod::HughesWinget)
    stress_old.rotate(_rotation_increment[_qp]);

  for (const auto i : make_range(_aqNTENS))
  {
    const auto a = component[i].first;
    const auto b = component[i].second;
    _aqSTRESS[i] = stress_old(a, b);
    _aqSTRAN[i] = _total_strain_old[_qp](a, b) * strain_factor[i];
    _aqDSTRAN[i] = _strain_increment[_qp](a, b) * strain_factor[i];
  }

  // current coordinates
  for (const auto i : make_range(Moose::dim))
    _aqCOORDS[i] = _q_point[_qp](i);

  // zero out Jacobian contribution
  for (const auto i : make_range(_aqNTENS * _aqNTENS))
    _aqDDSDDE[i] = 0.0;

  // Set PNEWDT initially to a large value
  _aqPNEWDT = std::numeric_limits<Real>::max();

  // Temperature
  _aqTEMP = _temperature_old[_qp];

  // Temperature increment
  _aqDTEMP = _temperature[_qp] - _temperature_old[_qp];

  for (const auto i : make_range(_number_external_fields))
  {
    // External field at this step
    _aqPREDEF[i] = (*_external_fields_old[i])[_qp];

    // External field increments
    _aqDPRED[i] = (*_external_fields[i])[_qp] - (*_external_fields_old[i])[_qp];
  }

  for (const auto i : make_range(_number_external_properties))
  {
    // External property at this step
    _aqPREDEF[i + _number_external_fields] = (*_external_properties_old[i])[_qp];

    // External property increments
    _aqDPRED[i + _number_external_fields] =
        (*_external_properties[i])[_qp] - (*_external_properties_old[i])[_qp];
  }

  // Layer number (not supported)
  _aqLAYER = -1;

  // Section point number within the layer (not supported)
  _aqKSPT = -1;

  // Increment number
  _aqKINC = _t_step;
  _aqKSTEP = 1;

  // integration point number
  _aqNPT = _qp + (_use_one_based_indexing ? 1 : 0);

  // Connection to extern statement
  _umat(_aqSTRESS.data(),
        _aqSTATEV.data(),
        _aqDDSDDE.data(),
        &_elastic_strain_energy[_qp],
        &_plastic_dissipation[_qp],
        &_creep_dissipation[_qp],
        &_aqRPL,
        _aqDDSDDT.data(),
        _aqDRPLDE.data(),
        &_aqDRPLDT,
        _aqSTRAN.data(),
        _aqDSTRAN.data(),
        _aqTIME.data(),
        &_aqDTIME,
        &_aqTEMP,
        &_aqDTEMP,
        _aqPREDEF.data(),
        _aqDPRED.data(),
        _aqCMNAME,
        &_aqNDI,
        &_aqNSHR,
        &_aqNTENS,
        &_aqNSTATV,
        _aqPROPS.data(),
        &_aqNPROPS,
        _aqCOORDS.data(),
        _aqDROT.data(),
        &_aqPNEWDT,
        &_aqCELENT,
        _aqDFGRD0.data(),
        _aqDFGRD1.data(),
        &_aqNOEL,
        &_aqNPT,
        &_aqLAYER,
        &_aqKSPT,
        &_aqKSTEP,
        &_aqKINC);

  // Update state variables
  for (int i = 0; i < _aqNSTATV; ++i)
    _state_var[_qp][i] = _aqSTATEV[i];

  // Here, we apply UMAT convention: Always multiply _dt by PNEWDT to determine the material time
  // step MOOSE time stepper will choose the most limiting of all material time step increments
  // provided
  _material_timestep[_qp] = _aqPNEWDT * _dt;

  // Get new stress tensor - UMAT should update stress
  // Account for difference in vector order convention: yz, xz, xy (MOOSE)  vs xy, xz, yz
  // (commercial software)
  _stress[_qp] = RankTwoTensor(
      _aqSTRESS[0], _aqSTRESS[1], _aqSTRESS[2], _aqSTRESS[5], _aqSTRESS[4], _aqSTRESS[3]);

  // Rotate the stress state to the current configuration, unless HughesWinget
  if (_decomposition_method != DecompMethod::HughesWinget)
    _stress[_qp].rotate(_rotation_increment[_qp]);

  // Build Jacobian matrix from UMAT's Voigt non-standard order to fourth order tensor.
  const unsigned int N = Moose::dim;
  const unsigned int ntens = N * (N + 1) / 2;
  const int nskip = N - 1;

  for (const auto i : make_range(N))
    for (const auto j : make_range(N))
      for (const auto k : make_range(N))
        for (const auto l : make_range(N))
        {
          if (i == j)
            _jacobian_mult[_qp](i, j, k, l) =
                k == l ? _aqDDSDDE[i * ntens + k] : _aqDDSDDE[i * ntens + k + nskip + l];
          else
            // i!=j
            _jacobian_mult[_qp](i, j, k, l) =
                k == l ? _aqDDSDDE[(nskip + i + j) * ntens + k]
                       : _aqDDSDDE[(nskip + i + j) * ntens + k + nskip + l];
        }
}
<|endoftext|>


<commit_before>#include <itkImageRegionConstIterator.h>

#include "rtkSheppLoganPhantomFilter.h"
#include "rtkDrawSheppLoganFilter.h"
#include "rtkFDKConeBeamReconstructionFilter.h"
#include "rtkConstantImageSource.h"
#include "rtkDisplacedDetectorImageFilter.h"

template<class TImage>
void CheckImageQuality(typename TImage::Pointer recon, typename TImage::Pointer ref)
{
  typedef itk::ImageRegionConstIterator<TImage> ImageIteratorType;
  ImageIteratorType itTest( recon, recon->GetBufferedRegion() );
  ImageIteratorType itRef( ref, ref->GetBufferedRegion() );

  typedef double ErrorType;
  ErrorType TestError = 0.;
  ErrorType EnerError = 0.;

  itTest.GoToBegin();
  itRef.GoToBegin();

  while( !itRef.IsAtEnd() )
    {
    typename TImage::PixelType TestVal = itTest.Get();
    typename TImage::PixelType RefVal = itRef.Get();
    TestError += vcl_abs(RefVal - TestVal);
    EnerError += vcl_pow(ErrorType(RefVal - TestVal), 2.);
    ++itTest;
    ++itRef;
    }
  // Error per Pixel
  ErrorType ErrorPerPixel = TestError/ref->GetBufferedRegion().GetNumberOfPixels();
  std::cout << "\nError per Pixel = " << ErrorPerPixel << std::endl;
  // MSE
  ErrorType MSE = EnerError/ref->GetBufferedRegion().GetNumberOfPixels();
  std::cout << "MSE = " << MSE << std::endl;
  // PSNR
  ErrorType PSNR = 20*log10(2.0) - 10*log10(MSE);
  std::cout << "PSNR = " << PSNR << "dB" << std::endl;
  // QI
  ErrorType QI = (2.0-ErrorPerPixel)/2.0;
  std::cout << "QI = " << QI << std::endl;

  // Checking results
  if (ErrorPerPixel > 0.061)
  {
    std::cerr << "Test Failed, Error per pixel not valid! "
              << ErrorPerPixel << " instead of 0.06." << std::endl;
    exit( EXIT_FAILURE);
  }
  if (PSNR < 24.)
  {
    std::cerr << "Test Failed, PSNR not valid! "
              << PSNR << " instead of 24." << std::endl;
    exit( EXIT_FAILURE);
  }
}

int main(int argc, char* argv[])
{
  const unsigned int Dimension = 3;
  typedef float                                    OutputPixelType;
  typedef itk::Image< OutputPixelType, Dimension > OutputImageType;
  const unsigned int NumberOfProjectionImages = 180;

  // Constant image sources
  typedef rtk::ConstantImageSource< OutputImageType > ConstantImageSourceType;
  ConstantImageSourceType::PointType origin;
  ConstantImageSourceType::SizeType size;
  ConstantImageSourceType::SpacingType spacing;

  ConstantImageSourceType::Pointer tomographySource  = ConstantImageSourceType::New();
  origin[0] = -127.;
  origin[1] = -127.;
  origin[2] = -127.;
  size[0] = 128;
  size[1] = 128;
  size[2] = 128;
  spacing[0] = 2.;
  spacing[1] = 2.;
  spacing[2] = 2.;
  tomographySource->SetOrigin( origin );
  tomographySource->SetSpacing( spacing );
  tomographySource->SetSize( size );
  tomographySource->SetConstant( 0. );

  ConstantImageSourceType::Pointer projectionsSource = ConstantImageSourceType::New();
  origin[0] = -254.;
  origin[1] = -254.;
  origin[2] = -254.;
  size[0] = 128;
  size[1] = 128;
  size[2] = NumberOfProjectionImages;
  spacing[0] = 4.;
  spacing[1] = 4.;
  spacing[2] = 4.;
  projectionsSource->SetOrigin( origin );
  projectionsSource->SetSpacing( spacing );
  projectionsSource->SetSize( size );
  projectionsSource->SetConstant( 0. );

  // Shepp Logan projections filter
  typedef rtk::SheppLoganPhantomFilter<OutputImageType, OutputImageType> SLPType;
  SLPType::Pointer slp=SLPType::New();
  slp->SetInput( projectionsSource->GetOutput() );

  // Displaced detector weighting
  typedef rtk::DisplacedDetectorImageFilter< OutputImageType > DDFType;
  DDFType::Pointer ddf = DDFType::New();
  ddf->SetInput( slp->GetOutput() );

  // Create a reference object (in this case a 3D phantom reference).
  typedef rtk::DrawSheppLoganFilter<OutputImageType, OutputImageType> DSLType;
  DSLType::Pointer dsl = DSLType::New();
  dsl->SetInput( tomographySource->GetOutput() );
  TRY_AND_EXIT_ON_ITK_EXCEPTION( dsl->Update() );

  // FDK reconstruction filtering
  typedef rtk::FDKConeBeamReconstructionFilter< OutputImageType > FDKCPUType;
  FDKCPUType::Pointer feldkamp = FDKCPUType::New();
  feldkamp->SetInput( 0, tomographySource->GetOutput() );
  feldkamp->SetInput( 1, ddf->GetOutput() );

  std::cout << "\n\n****** Case 1: positive offset in geometry ******" << std::endl;
  typedef rtk::ThreeDCircularProjectionGeometry GeometryType;
  GeometryType::Pointer geometry = GeometryType::New();
  slp->SetGeometry(geometry);
  ddf->SetGeometry( geometry );
  feldkamp->SetGeometry( geometry );
  for(unsigned int noProj=0; noProj<NumberOfProjectionImages; noProj++)
    geometry->AddProjection(600., 1200., noProj*360./NumberOfProjectionImages, 120., 0.);
  slp->Update();
  TRY_AND_EXIT_ON_ITK_EXCEPTION( feldkamp->Update() );
  CheckImageQuality<OutputImageType>(feldkamp->GetOutput(), dsl->GetOutput());

  std::cout << "\n\n****** Case 2: negative offset in geometry ******" << std::endl;
  geometry = GeometryType::New();
  slp->SetGeometry(geometry);
  ddf->SetGeometry( geometry );
  feldkamp->SetGeometry( geometry );
  for(unsigned int noProj=0; noProj<NumberOfProjectionImages; noProj++)
    geometry->AddProjection(600., 1200., noProj*360./NumberOfProjectionImages, -120., 0.);
  TRY_AND_EXIT_ON_ITK_EXCEPTION( feldkamp->Update() );
  CheckImageQuality<OutputImageType>(feldkamp->GetOutput(), dsl->GetOutput());

  std::cout << "\n\n****** Case 3: no displacement ******" << std::endl;
  geometry = GeometryType::New();
  slp->SetGeometry(geometry);
  ddf->SetGeometry( geometry );
  feldkamp->SetGeometry( geometry );
  for(unsigned int noProj=0; noProj<NumberOfProjectionImages; noProj++)
    geometry->AddProjection(600., 1200., noProj*360./NumberOfProjectionImages);
  projectionsSource->SetOrigin(origin);
  TRY_AND_EXIT_ON_ITK_EXCEPTION( feldkamp->Update() );
  CheckImageQuality<OutputImageType>(feldkamp->GetOutput(), dsl->GetOutput());

  std::cout << "\n\n****** Case 4: negative offset in origin ******" << std::endl;
  geometry = GeometryType::New();
  slp->SetGeometry(geometry);
  ddf->SetGeometry( geometry );
  feldkamp->SetGeometry( geometry );
  for(unsigned int noProj=0; noProj<NumberOfProjectionImages; noProj++)
    geometry->AddProjection(600., 1200., noProj*360./NumberOfProjectionImages);
  origin[0] = -400;
  projectionsSource->SetOrigin(origin);
  TRY_AND_EXIT_ON_ITK_EXCEPTION( feldkamp->Update() );
  CheckImageQuality<OutputImageType>(feldkamp->GetOutput(), dsl->GetOutput());

  std::cout << "\n\n****** Case 5: positive offset in origin ******" << std::endl;
  origin[0] = -100;
  projectionsSource->SetOrigin(origin);
  TRY_AND_EXIT_ON_ITK_EXCEPTION( feldkamp->Update() );
  CheckImageQuality<OutputImageType>(feldkamp->GetOutput(), dsl->GetOutput());

  std::cout << "\n\nTest PASSED! " << std::endl;
  exit(EXIT_SUCCESS);
}
<commit_msg>Coding style adapted to ITK convention<commit_after>#include <itkImageRegionConstIterator.h>

#include "rtkSheppLoganPhantomFilter.h"
#include "rtkDrawSheppLoganFilter.h"
#include "rtkFDKConeBeamReconstructionFilter.h"
#include "rtkConstantImageSource.h"
#include "rtkDisplacedDetectorImageFilter.h"

template<class TImage>
void CheckImageQuality(typename TImage::Pointer recon, typename TImage::Pointer ref)
{
  typedef itk::ImageRegionConstIterator<TImage> ImageIteratorType;
  ImageIteratorType itTest( recon, recon->GetBufferedRegion() );
  ImageIteratorType itRef( ref, ref->GetBufferedRegion() );

  typedef double ErrorType;
  ErrorType TestError = 0.;
  ErrorType EnerError = 0.;

  itTest.GoToBegin();
  itRef.GoToBegin();

  while( !itRef.IsAtEnd() )
    {
    typename TImage::PixelType TestVal = itTest.Get();
    typename TImage::PixelType RefVal = itRef.Get();
    TestError += vcl_abs(RefVal - TestVal);
    EnerError += vcl_pow(ErrorType(RefVal - TestVal), 2.);
    ++itTest;
    ++itRef;
    }
  // Error per Pixel
  ErrorType ErrorPerPixel = TestError/ref->GetBufferedRegion().GetNumberOfPixels();
  std::cout << "\nError per Pixel = " << ErrorPerPixel << std::endl;
  // MSE
  ErrorType MSE = EnerError/ref->GetBufferedRegion().GetNumberOfPixels();
  std::cout << "MSE = " << MSE << std::endl;
  // PSNR
  ErrorType PSNR = 20*log10(2.0) - 10*log10(MSE);
  std::cout << "PSNR = " << PSNR << "dB" << std::endl;
  // QI
  ErrorType QI = (2.0-ErrorPerPixel)/2.0;
  std::cout << "QI = " << QI << std::endl;

  // Checking results
  if (ErrorPerPixel > 0.061)
  {
    std::cerr << "Test Failed, Error per pixel not valid! "
              << ErrorPerPixel << " instead of 0.06." << std::endl;
    exit( EXIT_FAILURE);
  }
  if (PSNR < 24.)
  {
    std::cerr << "Test Failed, PSNR not valid! "
              << PSNR << " instead of 24." << std::endl;
    exit( EXIT_FAILURE);
  }
}

int main(int argc, char* argv[])
{
  const unsigned int Dimension = 3;
  typedef float                                    OutputPixelType;
  typedef itk::Image< OutputPixelType, Dimension > OutputImageType;
  const unsigned int NumberOfProjectionImages = 180;

  // Constant image sources
  typedef rtk::ConstantImageSource< OutputImageType > ConstantImageSourceType;
  ConstantImageSourceType::PointType origin;
  ConstantImageSourceType::SizeType size;
  ConstantImageSourceType::SpacingType spacing;

  ConstantImageSourceType::Pointer tomographySource  = ConstantImageSourceType::New();
  origin[0] = -127.;
  origin[1] = -127.;
  origin[2] = -127.;
  size[0] = 128;
  size[1] = 128;
  size[2] = 128;
  spacing[0] = 2.;
  spacing[1] = 2.;
  spacing[2] = 2.;
  tomographySource->SetOrigin( origin );
  tomographySource->SetSpacing( spacing );
  tomographySource->SetSize( size );
  tomographySource->SetConstant( 0. );

  ConstantImageSourceType::Pointer projectionsSource = ConstantImageSourceType::New();
  origin[0] = -254.;
  origin[1] = -254.;
  origin[2] = -254.;
  size[0] = 128;
  size[1] = 128;
  size[2] = NumberOfProjectionImages;
  spacing[0] = 4.;
  spacing[1] = 4.;
  spacing[2] = 4.;
  projectionsSource->SetOrigin( origin );
  projectionsSource->SetSpacing( spacing );
  projectionsSource->SetSize( size );
  projectionsSource->SetConstant( 0. );

  // Shepp Logan projections filter
  typedef rtk::SheppLoganPhantomFilter<OutputImageType, OutputImageType> SLPType;
  SLPType::Pointer slp = SLPType::New();
  slp->SetInput( projectionsSource->GetOutput() );

  // Displaced detector weighting
  typedef rtk::DisplacedDetectorImageFilter<OutputImageType> DDFType;
  DDFType::Pointer ddf = DDFType::New();
  ddf->SetInput( slp->GetOutput() );

  // Create a reference object (in this case a 3D phantom reference).
  typedef rtk::DrawSheppLoganFilter<OutputImageType, OutputImageType> DSLType;
  DSLType::Pointer dsl = DSLType::New();
  dsl->SetInput( tomographySource->GetOutput() );
  TRY_AND_EXIT_ON_ITK_EXCEPTION( dsl->Update() );

  // FDK reconstruction filtering
  typedef rtk::FDKConeBeamReconstructionFilter<OutputImageType> FDKCPUType;
  FDKCPUType::Pointer feldkamp = FDKCPUType::New();
  feldkamp->SetInput( 0, tomographySource->GetOutput() );
  feldkamp->SetInput( 1, ddf->GetOutput() );

  std::cout << "\n\n****** Case 1: positive offset in geometry ******" << std::endl;
  typedef rtk::ThreeDCircularProjectionGeometry GeometryType;
  GeometryType::Pointer geometry = GeometryType::New();
  slp->SetGeometry(geometry);
  ddf->SetGeometry( geometry );
  feldkamp->SetGeometry( geometry );
  for(unsigned int noProj=0; noProj<NumberOfProjectionImages; noProj++)
    geometry->AddProjection(600., 1200., noProj*360./NumberOfProjectionImages, 120., 0.);
  slp->Update();
  TRY_AND_EXIT_ON_ITK_EXCEPTION( feldkamp->Update() );
  CheckImageQuality<OutputImageType>(feldkamp->GetOutput(), dsl->GetOutput());

  std::cout << "\n\n****** Case 2: negative offset in geometry ******" << std::endl;
  geometry = GeometryType::New();
  slp->SetGeometry(geometry);
  ddf->SetGeometry( geometry );
  feldkamp->SetGeometry( geometry );
  for(unsigned int noProj=0; noProj<NumberOfProjectionImages; noProj++)
    geometry->AddProjection(600., 1200., noProj*360./NumberOfProjectionImages, -120., 0.);
  TRY_AND_EXIT_ON_ITK_EXCEPTION( feldkamp->Update() );
  CheckImageQuality<OutputImageType>(feldkamp->GetOutput(), dsl->GetOutput());

  std::cout << "\n\n****** Case 3: no displacement ******" << std::endl;
  geometry = GeometryType::New();
  slp->SetGeometry(geometry);
  ddf->SetGeometry( geometry );
  feldkamp->SetGeometry( geometry );
  for(unsigned int noProj=0; noProj<NumberOfProjectionImages; noProj++)
    geometry->AddProjection(600., 1200., noProj*360./NumberOfProjectionImages);
  projectionsSource->SetOrigin(origin);
  TRY_AND_EXIT_ON_ITK_EXCEPTION( feldkamp->Update() );
  CheckImageQuality<OutputImageType>(feldkamp->GetOutput(), dsl->GetOutput());

  std::cout << "\n\n****** Case 4: negative offset in origin ******" << std::endl;
  geometry = GeometryType::New();
  slp->SetGeometry(geometry);
  ddf->SetGeometry( geometry );
  feldkamp->SetGeometry( geometry );
  for(unsigned int noProj=0; noProj<NumberOfProjectionImages; noProj++)
    geometry->AddProjection(600., 1200., noProj*360./NumberOfProjectionImages);
  origin[0] = -400;
  projectionsSource->SetOrigin(origin);
  TRY_AND_EXIT_ON_ITK_EXCEPTION( feldkamp->Update() );
  CheckImageQuality<OutputImageType>(feldkamp->GetOutput(), dsl->GetOutput());

  std::cout << "\n\n****** Case 5: positive offset in origin ******" << std::endl;
  origin[0] = -100;
  projectionsSource->SetOrigin(origin);
  TRY_AND_EXIT_ON_ITK_EXCEPTION( feldkamp->Update() );
  CheckImageQuality<OutputImageType>(feldkamp->GetOutput(), dsl->GetOutput());

  std::cout << "\n\nTest PASSED! " << std::endl;
  exit(EXIT_SUCCESS);
}
<|endoftext|>