<commit_before>/*
 *  alarmresource.cpp  -  base class for a KAlarm alarm calendar resource
 *  Program:  kalarm
 *  Copyright © 2006,2007 by David Jarvie <software@astrojar.org.uk>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "kalarm.h"

#include <QFile>

#include <kconfig.h>
#include <kconfiggroup.h>
#include <klocale.h>
#include <kstandarddirs.h>
#include <kabc/lock.h>
#include <kabc/locknull.h>
#include <kcal/calendarlocal.h>

#include "alarmresource.moc"
using namespace KCal;
#include "alarmresources.h"

void              (*AlarmResource::mCalIDFunction)(CalendarLocal&) = 0;
KCalendar::Status (*AlarmResource::mFixFunction)(CalendarLocal&, const QString&, AlarmResource*, FixFunc) = 0;
int                 AlarmResource::mDebugArea = 0;
bool                AlarmResource::mNoGui = false;

AlarmResource::AlarmResource()
	: ResourceCached(),
	  mLock(0),
	  mType(static_cast<Type>(0)),    // invalid
	  mStandard(false),
	  mCloseAfterSave(false),
	  mCompatibility(KCalendar::Incompatible),
	  mReconfiguring(0),
	  mLoaded(false),
	  mLoading(false)
{
	// Prevent individual events being set read-only when loading a read-only resource
	setNoReadOnlyOnLoad(true);
	enableChangeNotification();
}

AlarmResource::AlarmResource(const KConfigGroup& group)
	: ResourceCached(group),
	  mLock(0),
	  mType(static_cast<Type>(0)),    // invalid
	  mStandard(false),
	  mCloseAfterSave(false),
	  mCompatibility(KCalendar::Incompatible),
	  mReconfiguring(0),
	  mLoaded(false),
	  mLoading(false)
{
	// Prevent individual events being set read-only when loading a read-only resource
	setNoReadOnlyOnLoad(true);

	ResourceCached::readConfig(group);
	int type = group.readEntry("AlarmType", static_cast<int>(ACTIVE));
	switch (type)
	{
		case ACTIVE:
		case ARCHIVED:
		case TEMPLATE:
			mType = static_cast<Type>(type);
			mStandard = group.readEntry("Standard", true);
			break;
		default:
			break;
	}
	mColour = group.readEntry("Color", QColor());
	enableChangeNotification();
}

AlarmResource::AlarmResource(Type type)
	: ResourceCached(),
	  mLock(0),
	  mType(type),
	  mStandard(false),
	  mCloseAfterSave(false),
	  mCompatibility(KCalendar::Incompatible),
	  mReconfiguring(0),
	  mLoaded(false),
	  mLoading(false)
{
	enableChangeNotification();
}

AlarmResource::~AlarmResource()
{
	delete mLock;
}

void AlarmResource::writeConfig(KConfigGroup& group)
{
	group.writeEntry("AlarmType", static_cast<int>(mType));
	if (mColour.isValid())
		group.writeEntry("Color", mColour);
	else
		group.deleteEntry("Color");
	group.writeEntry("Standard", mStandard);
	ResourceCached::writeConfig(group);
	ResourceCalendar::writeConfig(group);
}

void AlarmResource::startReconfig()
{
	mOldReadOnly = ResourceCached::readOnly();
	mNewReadOnly = mOldReadOnly;
	mReconfiguring = 1;
}

void AlarmResource::applyReconfig()
{
	if (!mReconfiguring)
		return;
	if (mReconfiguring == 1)
	{
		// Called before derived classes do their stuff
		ResourceCached::setReadOnly(mNewReadOnly);
		mReconfiguring = 2;
	}
	else
	{
		// Called when derived classes have done their stuff
		setReadOnly(mNewReadOnly);
		mReconfiguring = 0;
	}
}

/******************************************************************************
* If a function is defined to convert alarms to the current format, call it.
* Set the resource to read-only if it isn't the current format version, or if
* its format is unknown.
*/
void AlarmResource::checkCompatibility(const QString& filename)
{
	mCompatibility = KCalendar::Incompatible;   // assume the worst
	if (!mFixFunction)
		return;
	// Check whether the version is compatible (and convert it if desired)
	mCompatibility = (*mFixFunction)(*calendar(), filename, this, PROMPT);
	if (mCompatibility == KCalendar::Converted)
	{
		// Set mCompatibility first to ensure that readOnly() returns
		// the correct value and that save() therefore works.
		mCompatibility = KCalendar::Current;
		save();
	}
	if (mCompatibility != KCalendar::Current  &&  mCompatibility != KCalendar::ByEvent)
	{
		// It's not in the current KAlarm format, so it will be read-only to prevent incompatible updates
		kDebug(KARES_DEBUG) << "AlarmResource::checkCompatibility(" << resourceName() << "): opened read-only (not current KAlarm format)";
	}
}

/******************************************************************************
* If a function is defined to convert alarms to the current format, call it to
* convert an individual file within the overall resource.
*/
KCalendar::Status AlarmResource::checkCompatibility(CalendarLocal& calendar, const QString& filename, FixFunc conv)
{
	KCalendar::Status compat = KCalendar::Incompatible;   // assume the worst
	if (mFixFunction)
	{
		// Check whether the version is compatible (and convert it if desired)
		compat = (*mFixFunction)(calendar, filename, this, conv);
		if (compat == KCalendar::Converted)
			calendar.save(filename);
	}
	return compat;
}

KCalendar::Status AlarmResource::compatibility(const Event* event) const
{
	if (mCompatibility != KCalendar::ByEvent)
		return mCompatibility;
	CompatibilityMap::ConstIterator it = mCompatibilityMap.find(event);
	if (it == mCompatibilityMap.end())
		return KCalendar::Incompatible;    // event not found!?! - assume the worst
	return it.value();
}

bool AlarmResource::writable() const
{
	return isActive()  &&  !KCal::ResourceCached::readOnly()
	   &&  (mCompatibility == KCalendar::Current || mCompatibility == KCalendar::ByEvent);
}

bool AlarmResource::writable(const Event* event) const
{
	return isActive()  &&  !KCal::ResourceCached::readOnly()
	   &&  compatibility(event) == KCalendar::Current;
}

bool AlarmResource::readOnly() const
{
	return KCal::ResourceCached::readOnly()
	   ||  (isActive()  &&  mCompatibility != KCalendar::Current && mCompatibility != KCalendar::ByEvent);
}

void AlarmResource::setReadOnly(bool ronly)
{
	if (mReconfiguring == 1)
	{
		mNewReadOnly = ronly;
		return;
	}
	kDebug(KARES_DEBUG) << "AlarmResource::setReadOnly(" << ronly << ")";
	bool oldRCronly = (mReconfiguring == 2) ? mOldReadOnly : ResourceCached::readOnly();
	bool oldronly = (oldRCronly || (mCompatibility != KCalendar::Current && mCompatibility != KCalendar::ByEvent));
	if (!ronly  &&  isActive())
	{
		// Trying to change the resource to read-write.
		// Only allow this if it is in, or can be converted to, the current KAlarm format.
		switch (mCompatibility)
		{
			case KCalendar::Incompatible:
				emit notWritable(this);    // allow an error message to be output
				return;
			case KCalendar::Convertible:
				if (mReconfiguring <= 2)
				{
					if (!isOpen())
						return;
					load(NoSyncCache);   // give user the option of converting it
				}
				if (mCompatibility != KCalendar::Current)
					return;    // not converted, so keep as read-only
				break;
			case KCalendar::Current:
			case KCalendar::ByEvent:
			case KCalendar::Converted:   // shouldn't ever happen
				break;
		}
	}
	if (ronly != oldRCronly)
		ResourceCached::setReadOnly(ronly);
	if ((ronly || mCompatibility != KCalendar::Current && mCompatibility != KCalendar::ByEvent) != oldronly)
		emit readOnlyChanged(this);   // the effective read-only status has changed
}

void AlarmResource::setEnabled(bool enable)
{
	if (isActive() != enable)
	{
		setActive(enable);
		enableResource(enable);
		emit enabledChanged(this);
	}
}

void AlarmResource::setColour(const QColor& colour)
{
	if (colour != mColour)
	{
		mColour = colour;
		emit colourChanged(this);
	}
}

bool AlarmResource::load(CacheAction action)
{
	if (!ResourceCached::load(action))
		return false;
	emit resLoaded(this);    // special signal to AlarmResources
	return true;
}

bool AlarmResource::saveAndClose(CacheAction action, Incidence* incidence)
{
	bool result = save(action, incidence);
	if (isSaving())
		mCloseAfterSave = true;   // ensure it's closed if saving is asynchronous
	else
		close();
	return result;
}

void AlarmResource::doClose()
{
	mCloseAfterSave = false;
	KCal::ResourceCached::doClose();
	mLoaded = mLoading = false;
	mCompatibilityMap.clear();
}

QString AlarmResource::infoText() const
{
	KRES::Factory* factory = KRES::Factory::self("alarms");
	QString atype;
	switch (mType)
	{
		case ACTIVE:    atype = i18nc("@info/plain", "Active alarms");  break;
		case ARCHIVED:  atype = i18nc("@info/plain", "Archived alarms");  break;
		case TEMPLATE:  atype = i18nc("@info/plain", "Alarm templates");  break;
		default:        break;
	}
	QString perms = readOnly() ? i18nc("@info/plain", "Read-only") : i18nc("@info/plain", "Read-write");
	QString enabled = isActive() ? i18nc("@info/plain", "Enabled") : i18nc("@info/plain", "Disabled");
	QString std = (AlarmResources::instance()->getStandardResource(mType) == this) ? i18nc("@info/plain Parameter in 'Default resource: Yes/No'", "Yes") : i18nc("@info/plain Parameter in 'Default resource: Yes/No'", "No");
	return i18nc("@info",
	    "<title>%1</title>"
	    "<para>Resource type: %2
"
	    "Contents: %3
"
	    "%4: <filename>%5</filename>
"
	    "Permissions: %6
"
	    "Status: %7
"
	    "Default resource: %8</para>",
	    resourceName(), factory->typeName(type()), atype, displayType(), displayLocation(), perms, enabled, std);
}

void AlarmResource::lock(const QString& path)
{
	delete mLock;
	if (path.isNull())
		mLock = 0;
	else if (path.isEmpty())
		mLock = new KABC::LockNull(true);
	else
		mLock = new KABC::Lock(path);
}

KCalEvent::Status AlarmResource::kcalEventType() const
{
	switch (mType)
	{
		case ACTIVE:    return KCalEvent::ACTIVE;
		case ARCHIVED:  return KCalEvent::ARCHIVED;
		case TEMPLATE:  return KCalEvent::TEMPLATE;
		default:        return KCalEvent::EMPTY;
	}
}

/*
void AlarmResource::kaCheckCalendar(CalendarLocal& cal)
{
	mTypes = EMPTY;
	Event::List events = cal.rawEvents();
	for (Event::List::ConstIterator it = events.begin();  it != events.end();  ++it)
	{
		switch (KCalEvent::status(*it))
		{
			case KCalEvent::ACTIVE:    mTypes = static_cast<Type>(mTypes | ACTIVE);  break;
			case KCalEvent::ARCHIVED:  mTypes = static_cast<Type>(mTypes | ARCHIVED);  break;
			case KCalEvent::TEMPLATE:  mTypes = static_cast<Type>(mTypes | TEMPLATE);  break;
			default:   break;
		}
		if (mTypes == (ACTIVE | ARCHIVED | TEMPLATE))
			break;
	}
}
*/

#ifndef NDEBUG
QByteArray AlarmResource::typeName() const
{
	switch (mType)
	{
		case ACTIVE:    return "Active";
		case ARCHIVED:  return "Archived";
		case TEMPLATE:  return "Template";
		default:        return "Empty";
	}
}
#endif
<commit_msg>Fix crash when a resource is disabled<commit_after>/*
 *  alarmresource.cpp  -  base class for a KAlarm alarm calendar resource
 *  Program:  kalarm
 *  Copyright © 2006,2007 by David Jarvie <software@astrojar.org.uk>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "kalarm.h"

#include <QFile>

#include <kconfig.h>
#include <kconfiggroup.h>
#include <klocale.h>
#include <kstandarddirs.h>
#include <kabc/lock.h>
#include <kabc/locknull.h>
#include <kcal/calendarlocal.h>

#include "alarmresource.moc"
using namespace KCal;
#include "alarmresources.h"

void              (*AlarmResource::mCalIDFunction)(CalendarLocal&) = 0;
KCalendar::Status (*AlarmResource::mFixFunction)(CalendarLocal&, const QString&, AlarmResource*, FixFunc) = 0;
int                 AlarmResource::mDebugArea = 0;
bool                AlarmResource::mNoGui = false;

AlarmResource::AlarmResource()
	: ResourceCached(),
	  mLock(0),
	  mType(static_cast<Type>(0)),    // invalid
	  mStandard(false),
	  mCloseAfterSave(false),
	  mCompatibility(KCalendar::Incompatible),
	  mReconfiguring(0),
	  mLoaded(false),
	  mLoading(false)
{
	// Prevent individual events being set read-only when loading a read-only resource
	setNoReadOnlyOnLoad(true);
	enableChangeNotification();
}

AlarmResource::AlarmResource(const KConfigGroup& group)
	: ResourceCached(group),
	  mLock(0),
	  mType(static_cast<Type>(0)),    // invalid
	  mStandard(false),
	  mCloseAfterSave(false),
	  mCompatibility(KCalendar::Incompatible),
	  mReconfiguring(0),
	  mLoaded(false),
	  mLoading(false)
{
	// Prevent individual events being set read-only when loading a read-only resource
	setNoReadOnlyOnLoad(true);

	ResourceCached::readConfig(group);
	int type = group.readEntry("AlarmType", static_cast<int>(ACTIVE));
	switch (type)
	{
		case ACTIVE:
		case ARCHIVED:
		case TEMPLATE:
			mType = static_cast<Type>(type);
			mStandard = group.readEntry("Standard", true);
			break;
		default:
			break;
	}
	mColour = group.readEntry("Color", QColor());
	enableChangeNotification();
}

AlarmResource::AlarmResource(Type type)
	: ResourceCached(),
	  mLock(0),
	  mType(type),
	  mStandard(false),
	  mCloseAfterSave(false),
	  mCompatibility(KCalendar::Incompatible),
	  mReconfiguring(0),
	  mLoaded(false),
	  mLoading(false)
{
	enableChangeNotification();
}

AlarmResource::~AlarmResource()
{
	delete mLock;
}

void AlarmResource::writeConfig(KConfigGroup& group)
{
	group.writeEntry("AlarmType", static_cast<int>(mType));
	if (mColour.isValid())
		group.writeEntry("Color", mColour);
	else
		group.deleteEntry("Color");
	group.writeEntry("Standard", mStandard);
	ResourceCached::writeConfig(group);
	ResourceCalendar::writeConfig(group);
}

void AlarmResource::startReconfig()
{
	mOldReadOnly = ResourceCached::readOnly();
	mNewReadOnly = mOldReadOnly;
	mReconfiguring = 1;
}

void AlarmResource::applyReconfig()
{
	if (!mReconfiguring)
		return;
	if (mReconfiguring == 1)
	{
		// Called before derived classes do their stuff
		ResourceCached::setReadOnly(mNewReadOnly);
		mReconfiguring = 2;
	}
	else
	{
		// Called when derived classes have done their stuff
		setReadOnly(mNewReadOnly);
		mReconfiguring = 0;
	}
}

/******************************************************************************
* If a function is defined to convert alarms to the current format, call it.
* Set the resource to read-only if it isn't the current format version, or if
* its format is unknown.
*/
void AlarmResource::checkCompatibility(const QString& filename)
{
	mCompatibility = KCalendar::Incompatible;   // assume the worst
	if (!mFixFunction)
		return;
	// Check whether the version is compatible (and convert it if desired)
	mCompatibility = (*mFixFunction)(*calendar(), filename, this, PROMPT);
	if (mCompatibility == KCalendar::Converted)
	{
		// Set mCompatibility first to ensure that readOnly() returns
		// the correct value and that save() therefore works.
		mCompatibility = KCalendar::Current;
		save();
	}
	if (mCompatibility != KCalendar::Current  &&  mCompatibility != KCalendar::ByEvent)
	{
		// It's not in the current KAlarm format, so it will be read-only to prevent incompatible updates
		kDebug(KARES_DEBUG) << "AlarmResource::checkCompatibility(" << resourceName() << "): opened read-only (not current KAlarm format)";
	}
}

/******************************************************************************
* If a function is defined to convert alarms to the current format, call it to
* convert an individual file within the overall resource.
*/
KCalendar::Status AlarmResource::checkCompatibility(CalendarLocal& calendar, const QString& filename, FixFunc conv)
{
	KCalendar::Status compat = KCalendar::Incompatible;   // assume the worst
	if (mFixFunction)
	{
		// Check whether the version is compatible (and convert it if desired)
		compat = (*mFixFunction)(calendar, filename, this, conv);
		if (compat == KCalendar::Converted)
			calendar.save(filename);
	}
	return compat;
}

KCalendar::Status AlarmResource::compatibility(const Event* event) const
{
	if (mCompatibility != KCalendar::ByEvent)
		return mCompatibility;
	CompatibilityMap::ConstIterator it = mCompatibilityMap.find(event);
	if (it == mCompatibilityMap.end())
		return KCalendar::Incompatible;    // event not found!?! - assume the worst
	return it.value();
}

bool AlarmResource::writable() const
{
	return isActive()  &&  !KCal::ResourceCached::readOnly()
	   &&  (mCompatibility == KCalendar::Current || mCompatibility == KCalendar::ByEvent);
}

bool AlarmResource::writable(const Event* event) const
{
	return isActive()  &&  !KCal::ResourceCached::readOnly()
	   &&  compatibility(event) == KCalendar::Current;
}

bool AlarmResource::readOnly() const
{
	return KCal::ResourceCached::readOnly()
	   ||  (isActive()  &&  mCompatibility != KCalendar::Current && mCompatibility != KCalendar::ByEvent);
}

void AlarmResource::setReadOnly(bool ronly)
{
	if (mReconfiguring == 1)
	{
		mNewReadOnly = ronly;
		return;
	}
	kDebug(KARES_DEBUG) << "AlarmResource::setReadOnly(" << ronly << ")";
	bool oldRCronly = (mReconfiguring == 2) ? mOldReadOnly : ResourceCached::readOnly();
	bool oldronly = (oldRCronly || (mCompatibility != KCalendar::Current && mCompatibility != KCalendar::ByEvent));
	if (!ronly  &&  isActive())
	{
		// Trying to change the resource to read-write.
		// Only allow this if it is in, or can be converted to, the current KAlarm format.
		switch (mCompatibility)
		{
			case KCalendar::Incompatible:
				emit notWritable(this);    // allow an error message to be output
				return;
			case KCalendar::Convertible:
				if (mReconfiguring <= 2)
				{
					if (!isOpen())
						return;
					load(NoSyncCache);   // give user the option of converting it
				}
				if (mCompatibility != KCalendar::Current)
					return;    // not converted, so keep as read-only
				break;
			case KCalendar::Current:
			case KCalendar::ByEvent:
			case KCalendar::Converted:   // shouldn't ever happen
				break;
		}
	}
	if (ronly != oldRCronly)
		ResourceCached::setReadOnly(ronly);
	if ((ronly || mCompatibility != KCalendar::Current && mCompatibility != KCalendar::ByEvent) != oldronly)
		emit readOnlyChanged(this);   // the effective read-only status has changed
}

void AlarmResource::setEnabled(bool enable)
{
	if (isActive() != enable)
	{
		setActive(enable);
		enableResource(enable);
		emit enabledChanged(this);
	}
}

void AlarmResource::setColour(const QColor& colour)
{
	if (colour != mColour)
	{
		mColour = colour;
		emit colourChanged(this);
	}
}

bool AlarmResource::load(CacheAction action)
{
	if (!ResourceCached::load(action))
		return false;
	emit resLoaded(this);    // special signal to AlarmResources
	return true;
}

bool AlarmResource::saveAndClose(CacheAction action, Incidence* incidence)
{
	bool result = save(action, incidence);
	if (isSaving())
		mCloseAfterSave = true;   // ensure it's closed if saving is asynchronous
	else
		close();
	return result;
}

void AlarmResource::doClose()
{
	mCloseAfterSave = false;
	emit invalidate(this);
	KCal::ResourceCached::doClose();
	mLoaded = mLoading = false;
	mCompatibilityMap.clear();
}

QString AlarmResource::infoText() const
{
	KRES::Factory* factory = KRES::Factory::self("alarms");
	QString atype;
	switch (mType)
	{
		case ACTIVE:    atype = i18nc("@info/plain", "Active alarms");  break;
		case ARCHIVED:  atype = i18nc("@info/plain", "Archived alarms");  break;
		case TEMPLATE:  atype = i18nc("@info/plain", "Alarm templates");  break;
		default:        break;
	}
	QString perms = readOnly() ? i18nc("@info/plain", "Read-only") : i18nc("@info/plain", "Read-write");
	QString enabled = isActive() ? i18nc("@info/plain", "Enabled") : i18nc("@info/plain", "Disabled");
	QString std = (AlarmResources::instance()->getStandardResource(mType) == this) ? i18nc("@info/plain Parameter in 'Default resource: Yes/No'", "Yes") : i18nc("@info/plain Parameter in 'Default resource: Yes/No'", "No");
	return i18nc("@info",
	    "<title>%1</title>"
	    "<para>Resource type: %2
"
	    "Contents: %3
"
	    "%4: <filename>%5</filename>
"
	    "Permissions: %6
"
	    "Status: %7
"
	    "Default resource: %8</para>",
	    resourceName(), factory->typeName(type()), atype, displayType(), displayLocation(), perms, enabled, std);
}

void AlarmResource::lock(const QString& path)
{
	delete mLock;
	if (path.isNull())
		mLock = 0;
	else if (path.isEmpty())
		mLock = new KABC::LockNull(true);
	else
		mLock = new KABC::Lock(path);
}

KCalEvent::Status AlarmResource::kcalEventType() const
{
	switch (mType)
	{
		case ACTIVE:    return KCalEvent::ACTIVE;
		case ARCHIVED:  return KCalEvent::ARCHIVED;
		case TEMPLATE:  return KCalEvent::TEMPLATE;
		default:        return KCalEvent::EMPTY;
	}
}

/*
void AlarmResource::kaCheckCalendar(CalendarLocal& cal)
{
	mTypes = EMPTY;
	Event::List events = cal.rawEvents();
	for (Event::List::ConstIterator it = events.begin();  it != events.end();  ++it)
	{
		switch (KCalEvent::status(*it))
		{
			case KCalEvent::ACTIVE:    mTypes = static_cast<Type>(mTypes | ACTIVE);  break;
			case KCalEvent::ARCHIVED:  mTypes = static_cast<Type>(mTypes | ARCHIVED);  break;
			case KCalEvent::TEMPLATE:  mTypes = static_cast<Type>(mTypes | TEMPLATE);  break;
			default:   break;
		}
		if (mTypes == (ACTIVE | ARCHIVED | TEMPLATE))
			break;
	}
}
*/

#ifndef NDEBUG
QByteArray AlarmResource::typeName() const
{
	switch (mType)
	{
		case ACTIVE:    return "Active";
		case ARCHIVED:  return "Archived";
		case TEMPLATE:  return "Template";
		default:        return "Empty";
	}
}
#endif
<|endoftext|>


<commit_before>/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/
#include <map>

#include <boost/make_shared.hpp>

#include <qimessaging/api.hpp>
#include <qimessaging/genericvalue.hpp>
#include <qimessaging/type.hpp>
#include <qimessaging/genericvalue.hpp>
#include <qimessaging/genericobject.hpp>
#include <qimessaging/functiontype.hpp>
#include <qimessaging/metaobject.hpp>
#include <qimessaging/signal.hpp>
#include <qimessaging/dynamicobject.hpp>

namespace qi
{

  class DynamicObjectPrivate
  {
  public:
    // get or create signal, or 0 if id is not an event
    SignalBase* createSignal(unsigned int id);
    bool                                dying;
    typedef std::map<unsigned int, SignalBase*> SignalMap;
    typedef std::map<unsigned int, GenericFunction> MethodMap;
    SignalMap           signalMap;
    MethodMap           methodMap;
    MetaObject          meta;
  };

  SignalBase* DynamicObjectPrivate::createSignal(unsigned int id)
  {
    SignalMap::iterator i = signalMap.find(id);
    if (i != signalMap.end())
      return i->second;

    MetaSignal* sig = meta.signal(id);
    if (sig)
    {
      SignalBase* sb = new SignalBase(sig->signature());
      signalMap[id] = sb;
      return sb;
    }
    return 0;
  }

  class DynamicObjectType: public ObjectType, public DefaultTypeImplMethods<DynamicObject>
  {
  public:
    DynamicObjectType() {}
    virtual const MetaObject& metaObject(void* instance);
    virtual qi::Future<GenericValue> metaCall(void* instance, unsigned int method, const GenericFunctionParameters& params, MetaCallType callType = MetaCallType_Auto);
    virtual void metaEmit(void* instance, unsigned int signal, const GenericFunctionParameters& params);
    virtual qi::Future<unsigned int> connect(void* instance, unsigned int event, const SignalSubscriber& subscriber);
    /// Disconnect an event link. Returns if disconnection was successful.
    virtual qi::Future<void> disconnect(void* instance, unsigned int linkId);
    virtual Manageable* manageable(void* intance);
    virtual const std::vector<std::pair<Type*, int> >& parentTypes();

    _QI_BOUNCE_TYPE_METHODS(DefaultTypeImplMethods<DynamicObject>);
  };

  DynamicObject::DynamicObject()
  {
    _p = boost::make_shared<DynamicObjectPrivate>();
  }

  DynamicObject::~DynamicObject()
  {
  }

  void DynamicObject::setMetaObject(const MetaObject& m)
  {
    _p->meta = m;
    // We will populate stuff on demand
  }

  MetaObject& DynamicObject::metaObject()
  {
    return _p->meta;
  }

  void DynamicObject::setMethod(unsigned int id, GenericFunction callable)
  {
    _p->methodMap[id] = callable;
  }

  GenericFunction DynamicObject::method(unsigned int id)
  {
    DynamicObjectPrivate::MethodMap::iterator i = _p->methodMap.find(id);
    if (i == _p->methodMap.end())
      return GenericFunction();
    else
      return i->second;
  }

  SignalBase* DynamicObject::signalBase(unsigned int id) const
  {
    DynamicObjectPrivate::SignalMap::iterator i = _p->signalMap.find(id);
    if (i == _p->signalMap.end())
      return 0;
    else
      return i->second;
  }

  qi::Future<GenericValue> DynamicObject::metaCall(unsigned int method, const GenericFunctionParameters& params, MetaCallType callType)
  {
    qi::Promise<GenericValue> out;
    DynamicObjectPrivate::MethodMap::iterator i = _p->methodMap.find(method);
    if (i == _p->methodMap.end())
    {
      std::stringstream ss;
      ss << "Can't find methodID: " << method;
      out.setError(ss.str());
      return out.future();
    }

    return ::qi::metaCall(eventLoop(), i->second, params, callType);
  }

  void DynamicObject::metaEmit(unsigned int event, const GenericFunctionParameters& params)
  {
    SignalBase * s = _p->createSignal(event);
    if (s)
    { // signal is declared, create if needed
      s->trigger(params);
    }
    else
    {
      // Allow emit on a method
      // FIXME: call errors are lost
      if (metaObject().method(event))
        metaCall(event, params, MetaCallType_Auto);
      else
        qiLogError("object") << "No such event " << event;
    }
  }

  qi::Future<unsigned int> DynamicObject::connect(unsigned int event, const SignalSubscriber& subscriber)
  {
    SignalBase * s = _p->createSignal(event);
    if (!s)
      return qi::makeFutureError<unsigned int>("Cannot find signal");
    SignalBase::Link l = s->connect(subscriber);
     if (l > 0xFFFF)
      qiLogError("object") << "Signal id too big";
    return qi::Future<unsigned int>((event << 16) + l);
  }

  qi::Future<void> DynamicObject::disconnect(unsigned int linkId)
  {
    unsigned int event = linkId >> 16;
    unsigned int link = linkId & 0xFFFF;
    //TODO: weird to call createSignal in disconnect
    SignalBase* s = _p->createSignal(event);
    if (!s)
      return qi::makeFutureError<void>("Cannot find local signal connection.");
    bool b = s->disconnect(link);
    if (!b) {
      return qi::makeFutureError<void>("Cannot find local signal connection.");
    }
    return qi::Future<void>(0);
  }

  class MFunctorCall
  {
  public:
    MFunctorCall(GenericFunction& func, GenericFunctionParameters& params,
       qi::Promise<GenericValue>* out, bool noCloneFirst)
    : noCloneFirst(noCloneFirst)
    {
      this->out = out;
      std::swap(this->func, func);
      std::swap((std::vector<GenericValue>&) params,
        (std::vector<GenericValue>&) this->params);
    }
    MFunctorCall(const MFunctorCall& b)
    {
      (*this) = b;
    }
    void operator = (const MFunctorCall& b)
    {
      std::swap( (std::vector<GenericValue>&) params,
        (std::vector<GenericValue>&) b.params);
      std::swap(func, const_cast<MFunctorCall&>(b).func);
      this->out = b.out;
      noCloneFirst = b.noCloneFirst;
    }
    void operator()()
    {
      out->setValue(func.call(params));
      params.destroy(noCloneFirst);
      delete out;
    }
    qi::Promise<GenericValue>* out;
    GenericFunctionParameters params;
    GenericFunction func;
    bool noCloneFirst;
  };
  qi::Future<GenericValue> metaCall(EventLoop* el,
    GenericFunction func, const GenericFunctionParameters& params, MetaCallType callType, bool noCloneFirst)
  {
    bool synchronous = true;
    switch (callType)
    {
    case qi::MetaCallType_Direct:
      break;
    case qi::MetaCallType_Auto:
      synchronous = !el ||  el->isInEventLoopThread();
      break;
    case qi::MetaCallType_Queued:
      synchronous = !el;
      break;
    }
    if (synchronous)
    {
      qi::Promise<GenericValue> out;
      out.setValue(func.call(params));
      return out.future();
    }
    else
    {
      qi::Promise<GenericValue>* out = new qi::Promise<GenericValue>();
      GenericFunctionParameters pCopy = params.copy(noCloneFirst);
      qi::Future<GenericValue> result = out->future();
      el->asyncCall(0,
        MFunctorCall(func, pCopy, out, noCloneFirst));
      return result;
    }
  }

  //DynamicObjectType implementation: just bounces everything to metaobject

  const MetaObject& DynamicObjectType::metaObject(void* instance)
  {
    return reinterpret_cast<DynamicObject*>(instance)->metaObject();
  }

  qi::Future<GenericValue> DynamicObjectType::metaCall(void* instance, unsigned int method, const GenericFunctionParameters& params, MetaCallType callType)
  {
    return reinterpret_cast<DynamicObject*>(instance)
      ->metaCall(method, params, callType);
  }

  void DynamicObjectType::metaEmit(void* instance, unsigned int signal, const GenericFunctionParameters& params)
  {
    reinterpret_cast<DynamicObject*>(instance)
      ->metaEmit(signal, params);
  }

  qi::Future<unsigned int> DynamicObjectType::connect(void* instance, unsigned int event, const SignalSubscriber& subscriber)
  {
    return reinterpret_cast<DynamicObject*>(instance)
      ->connect(event, subscriber);
  }

  qi::Future<void> DynamicObjectType::disconnect(void* instance, unsigned int linkId)
  {
    return reinterpret_cast<DynamicObject*>(instance)
      ->disconnect(linkId);
  }

  const std::vector<std::pair<Type*, int> >& DynamicObjectType::parentTypes()
  {
    static std::vector<std::pair<Type*, int> > empty;
    return empty;
  }

  Manageable* DynamicObjectType::manageable(void* instance)
  {
    return reinterpret_cast<DynamicObject*>(instance);
  }

  ObjectPtr     makeDynamicObjectPtr(DynamicObject *obj)
  {
    ObjectPtr op;
    static DynamicObjectType* type = new DynamicObjectType();
    op = ObjectPtr(new GenericObject(type, obj));
    return op;
  }

}
<commit_msg>indent<commit_after>/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/
#include <map>

#include <boost/make_shared.hpp>

#include <qimessaging/api.hpp>
#include <qimessaging/genericvalue.hpp>
#include <qimessaging/type.hpp>
#include <qimessaging/genericvalue.hpp>
#include <qimessaging/genericobject.hpp>
#include <qimessaging/functiontype.hpp>
#include <qimessaging/metaobject.hpp>
#include <qimessaging/signal.hpp>
#include <qimessaging/dynamicobject.hpp>

namespace qi
{

  class DynamicObjectPrivate
  {
  public:
    // get or create signal, or 0 if id is not an event
    SignalBase* createSignal(unsigned int id);
    bool                                dying;
    typedef std::map<unsigned int, SignalBase*> SignalMap;
    typedef std::map<unsigned int, GenericFunction> MethodMap;
    SignalMap           signalMap;
    MethodMap           methodMap;
    MetaObject          meta;
  };

  SignalBase* DynamicObjectPrivate::createSignal(unsigned int id)
  {
    SignalMap::iterator i = signalMap.find(id);
    if (i != signalMap.end())
      return i->second;

    MetaSignal* sig = meta.signal(id);
    if (sig)
    {
      SignalBase* sb = new SignalBase(sig->signature());
      signalMap[id] = sb;
      return sb;
    }
    return 0;
  }

  class DynamicObjectType: public ObjectType, public DefaultTypeImplMethods<DynamicObject>
  {
  public:
    DynamicObjectType() {}
    virtual const MetaObject& metaObject(void* instance);
    virtual qi::Future<GenericValue> metaCall(void* instance, unsigned int method, const GenericFunctionParameters& params, MetaCallType callType = MetaCallType_Auto);
    virtual void metaEmit(void* instance, unsigned int signal, const GenericFunctionParameters& params);
    virtual qi::Future<unsigned int> connect(void* instance, unsigned int event, const SignalSubscriber& subscriber);
    /// Disconnect an event link. Returns if disconnection was successful.
    virtual qi::Future<void> disconnect(void* instance, unsigned int linkId);
    virtual Manageable* manageable(void* intance);
    virtual const std::vector<std::pair<Type*, int> >& parentTypes();

    _QI_BOUNCE_TYPE_METHODS(DefaultTypeImplMethods<DynamicObject>);
  };

  DynamicObject::DynamicObject()
  {
    _p = boost::make_shared<DynamicObjectPrivate>();
  }

  DynamicObject::~DynamicObject()
  {
  }

  void DynamicObject::setMetaObject(const MetaObject& m)
  {
    _p->meta = m;
    // We will populate stuff on demand
  }

  MetaObject& DynamicObject::metaObject()
  {
    return _p->meta;
  }

  void DynamicObject::setMethod(unsigned int id, GenericFunction callable)
  {
    _p->methodMap[id] = callable;
  }

  GenericFunction DynamicObject::method(unsigned int id)
  {
    DynamicObjectPrivate::MethodMap::iterator i = _p->methodMap.find(id);
    if (i == _p->methodMap.end())
      return GenericFunction();
    else
      return i->second;
  }

  SignalBase* DynamicObject::signalBase(unsigned int id) const
  {
    DynamicObjectPrivate::SignalMap::iterator i = _p->signalMap.find(id);
    if (i == _p->signalMap.end())
      return 0;
    else
      return i->second;
  }

  qi::Future<GenericValue> DynamicObject::metaCall(unsigned int method, const GenericFunctionParameters& params, MetaCallType callType)
  {
    qi::Promise<GenericValue> out;
    DynamicObjectPrivate::MethodMap::iterator i = _p->methodMap.find(method);
    if (i == _p->methodMap.end())
    {
      std::stringstream ss;
      ss << "Can't find methodID: " << method;
      out.setError(ss.str());
      return out.future();
    }

    return ::qi::metaCall(eventLoop(), i->second, params, callType);
  }

  void DynamicObject::metaEmit(unsigned int event, const GenericFunctionParameters& params)
  {
    SignalBase * s = _p->createSignal(event);
    if (s)
    { // signal is declared, create if needed
      s->trigger(params);
    }
    else
    {
      // Allow emit on a method
      // FIXME: call errors are lost
      if (metaObject().method(event))
        metaCall(event, params, MetaCallType_Auto);
      else
        qiLogError("object") << "No such event " << event;
    }
  }

  qi::Future<unsigned int> DynamicObject::connect(unsigned int event, const SignalSubscriber& subscriber)
  {
    SignalBase * s = _p->createSignal(event);
    if (!s)
      return qi::makeFutureError<unsigned int>("Cannot find signal");
    SignalBase::Link l = s->connect(subscriber);
    if (l > 0xFFFF)
      qiLogError("object") << "Signal id too big";
    return qi::Future<unsigned int>((event << 16) + l);
  }

  qi::Future<void> DynamicObject::disconnect(unsigned int linkId)
  {
    unsigned int event = linkId >> 16;
    unsigned int link = linkId & 0xFFFF;
    //TODO: weird to call createSignal in disconnect
    SignalBase* s = _p->createSignal(event);
    if (!s)
      return qi::makeFutureError<void>("Cannot find local signal connection.");
    bool b = s->disconnect(link);
    if (!b) {
      return qi::makeFutureError<void>("Cannot find local signal connection.");
    }
    return qi::Future<void>(0);
  }

  class MFunctorCall
  {
  public:
    MFunctorCall(GenericFunction& func, GenericFunctionParameters& params,
       qi::Promise<GenericValue>* out, bool noCloneFirst)
    : noCloneFirst(noCloneFirst)
    {
      this->out = out;
      std::swap(this->func, func);
      std::swap((std::vector<GenericValue>&) params,
        (std::vector<GenericValue>&) this->params);
    }
    MFunctorCall(const MFunctorCall& b)
    {
      (*this) = b;
    }
    void operator = (const MFunctorCall& b)
    {
      std::swap( (std::vector<GenericValue>&) params,
        (std::vector<GenericValue>&) b.params);
      std::swap(func, const_cast<MFunctorCall&>(b).func);
      this->out = b.out;
      noCloneFirst = b.noCloneFirst;
    }
    void operator()()
    {
      out->setValue(func.call(params));
      params.destroy(noCloneFirst);
      delete out;
    }
    qi::Promise<GenericValue>* out;
    GenericFunctionParameters params;
    GenericFunction func;
    bool noCloneFirst;
  };
  qi::Future<GenericValue> metaCall(EventLoop* el,
    GenericFunction func, const GenericFunctionParameters& params, MetaCallType callType, bool noCloneFirst)
  {
    bool synchronous = true;
    switch (callType)
    {
    case qi::MetaCallType_Direct:
      break;
    case qi::MetaCallType_Auto:
      synchronous = !el ||  el->isInEventLoopThread();
      break;
    case qi::MetaCallType_Queued:
      synchronous = !el;
      break;
    }
    if (synchronous)
    {
      qi::Promise<GenericValue> out;
      out.setValue(func.call(params));
      return out.future();
    }
    else
    {
      qi::Promise<GenericValue>* out = new qi::Promise<GenericValue>();
      GenericFunctionParameters pCopy = params.copy(noCloneFirst);
      qi::Future<GenericValue> result = out->future();
      el->asyncCall(0,
        MFunctorCall(func, pCopy, out, noCloneFirst));
      return result;
    }
  }

  //DynamicObjectType implementation: just bounces everything to metaobject

  const MetaObject& DynamicObjectType::metaObject(void* instance)
  {
    return reinterpret_cast<DynamicObject*>(instance)->metaObject();
  }

  qi::Future<GenericValue> DynamicObjectType::metaCall(void* instance, unsigned int method, const GenericFunctionParameters& params, MetaCallType callType)
  {
    return reinterpret_cast<DynamicObject*>(instance)
      ->metaCall(method, params, callType);
  }

  void DynamicObjectType::metaEmit(void* instance, unsigned int signal, const GenericFunctionParameters& params)
  {
    reinterpret_cast<DynamicObject*>(instance)
      ->metaEmit(signal, params);
  }

  qi::Future<unsigned int> DynamicObjectType::connect(void* instance, unsigned int event, const SignalSubscriber& subscriber)
  {
    return reinterpret_cast<DynamicObject*>(instance)
      ->connect(event, subscriber);
  }

  qi::Future<void> DynamicObjectType::disconnect(void* instance, unsigned int linkId)
  {
    return reinterpret_cast<DynamicObject*>(instance)
      ->disconnect(linkId);
  }

  const std::vector<std::pair<Type*, int> >& DynamicObjectType::parentTypes()
  {
    static std::vector<std::pair<Type*, int> > empty;
    return empty;
  }

  Manageable* DynamicObjectType::manageable(void* instance)
  {
    return reinterpret_cast<DynamicObject*>(instance);
  }

  ObjectPtr     makeDynamicObjectPtr(DynamicObject *obj)
  {
    ObjectPtr op;
    static DynamicObjectType* type = new DynamicObjectType();
    op = ObjectPtr(new GenericObject(type, obj));
    return op;
  }

}
<|endoftext|>


<commit_before>/*
** Author(s):
**  - Cedric GESTES <gestes@aldebaran-robotics.com>
**
** Copyright (C) 2012 Aldebaran Robotics
*/

#include <gtest/gtest.h>
#include <qimessaging/signal.hpp>
#include <qimessaging/future.hpp>
#include <qi/application.hpp>

class Foo
{
public:
  void func(int)               { }
  void func1(int *r, int)      { *r += 1; }
  void func2(int *r, int, int) { *r += 1; }
};
void foo(int *r, int, int)     { *r += 1; }
void foo2(int *r, char, char)  { *r += 1; }
void foo3(int *r, Foo *)       { *r += 1; }
void foolast(int, qi::Promise<void> prom) { prom.setValue(0); }

TEST(TestSignal, TestCompilation)
{
  int                    res = 0;
  qi::Signal<void (int)> s;
  Foo*                   f = 0;
  qi::Promise<void>      prom;

  //do not count
  s.connect(f, &Foo::func);
  s.connect(boost::bind<void>(&Foo::func, f, _1));

  s.connect(boost::bind(&foo, &res, 12, _1));
  s.connect(boost::bind(&foo2, &res, 12, _1));
  s.connect(boost::bind<void>(&Foo::func1, f, &res, _1));
  s.connect(boost::bind<void>(&Foo::func2, f, &res, 5, _1));
  s.connect(boost::bind(&foo3, &res, f));
  s.connect(boost::bind(&foolast, _1, prom));

  s(42);
  int timeout = 1000;
  while (timeout > 0) {
    qi::os::msleep(1);
    if (res == 5)
      break;
    timeout -= 1;
  }
  ASSERT_EQ(5, res);
  ASSERT_TRUE(prom.future().isReady());
  ASSERT_FALSE(prom.future().hasError());
}

int main(int argc, char **argv) {
  qi::Application app(argc, argv);
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<commit_msg>test_signal: More tests.<commit_after>/*
** Author(s):
**  - Cedric GESTES <gestes@aldebaran-robotics.com>
**
** Copyright (C) 2012 Aldebaran Robotics
*/

#include <gtest/gtest.h>
#include <qimessaging/signal.hpp>
#include <qimessaging/future.hpp>
#include <qi/application.hpp>

class Foo
{
public:
  void func(int)               { }
  void func1(int *r, int)      { *r += 1; }
  void func2(int *r, int, int) { *r += 1; }
};
void foo(int *r, int, int)     { *r += 1; }
void foo2(int *r, char, char)  { *r += 1; }
void foo3(int *r, Foo *)       { *r += 1; }
void foolast(int, qi::Promise<void> prom) { prom.setValue(0); }

TEST(TestSignal, TestCompilation)
{
  int                    res = 0;
  qi::Signal<void (int)> s;
  Foo*                   f = 0;
  qi::Promise<void>      prom;

  //do not count
  s.connect(qi::makeGenericFunction(f, &Foo::func));
  s.connect(boost::bind<void>(&Foo::func, f, _1));

  s.connect(boost::bind(&foo, &res, 12, _1));
  s.connect(boost::bind(&foo2, &res, 12, _1));
  s.connect(boost::bind<void>(&Foo::func1, f, &res, _1));
  s.connect(boost::bind<void>(&Foo::func2, f, &res, 5, _1));
  s.connect(boost::bind(&foo3, &res, f));
  s.connect(boost::bind(&foolast, _1, prom));

  s(42);
  int timeout = 1000;
  while (timeout > 0) {
    qi::os::msleep(1);
    if (res == 5)
      break;
    timeout -= 1;
  }
  ASSERT_EQ(5, res);
  ASSERT_TRUE(prom.future().isReady());
  ASSERT_FALSE(prom.future().hasError());
}

void write42(boost::shared_ptr<int> ptr)
{
  *ptr = 42;
}

TEST(TestSignal, SharedPtr)
{
  // Redundant with Copy test, but just to be sure, check that shared_ptr
  // is correctly transmited.
  qi::Signal<void (boost::shared_ptr<int>)> sig;
  sig.connect(qi::makeGenericFunction(&write42), qi::getDefaultObjectEventLoop());
  {
    boost::shared_ptr<int> ptr(new int(12));
    sig(ptr);
  };
}

void byRef(int& i, bool* done)
{
  qiLogDebug("test") <<"byRef " << &i;
  i = 12;
  *done = true;
}

TEST(TestSignal, Copy)
{
  // Check that reference argument type are copied when an async call is made
  qi::Signal<void (int&, bool*)> sig;
  qiLogDebug("test") << "sync";
  sig.connect(qi::makeGenericFunction(byRef), 0);
  bool done = false;
  int i = 0;
  sig(i, &done);
  ASSERT_TRUE(done); //synchronous
  ASSERT_EQ(12, i); // byref
  qiLogDebug("test") << "async";
  sig =  qi::Signal<void (int&, bool*)>();
  sig.connect(qi::makeGenericFunction(byRef), qi::getDefaultObjectEventLoop());
  i = 0;
  done = false;
  sig(i, &done);
  for (unsigned c=0; !done && c<100;++c) qi::os::msleep(10);
  ASSERT_TRUE(done);
  ASSERT_EQ(0, i); // async: was copied
}

int main(int argc, char **argv) {
  qi::Application app(argc, argv);
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////////////
//
// File ContField2D.cpp
//
// For more information, please see: http://www.nektar.info
//
// The MIT License
//
// Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
// Department of Aeronautics, Imperial College London (UK), and Scientific
// Computing and Imaging Institute, University of Utah (USA).
//
// License for the specific language governing rights and limitations under
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
// Description: Field definition for 2D domain with boundary conditions
//
///////////////////////////////////////////////////////////////////////////////

#include <MultiRegions/ContField2D.h>

namespace Nektar
{
    namespace MultiRegions
    {

        ContField2D::ContField2D(void):
            ContExpList2D(),
            m_bndCondExpansions(),
            m_bndConditions()
        {
        }

        ContField2D::ContField2D(const ContField2D &In):
            ContExpList2D(In),
            m_bndCondExpansions(In.m_bndCondExpansions),
            m_bndConditions(In.m_bndConditions)
        {
        }

        ContField2D::ContField2D(SpatialDomains::MeshGraph2D &graph2D,
                                 SpatialDomains::BoundaryConditions &bcs,
                                 const int bc_loc):
            ContExpList2D(graph2D,false)  ,
            m_bndCondExpansions(),
            m_bndConditions()
        {
            GenerateBoundaryConditionExpansion(graph2D,bcs,bcs.GetVariable(bc_loc));
            EvaluateBoundaryConditions();

            map<int,int> periodicEdges;
            map<int,int> periodicVertices;
            GetPeriodicEdges(graph2D,bcs,bcs.GetVariable(bc_loc),periodicVertices,periodicEdges);

            m_locToGloMap = MemoryManager<LocalToGlobalC0ContMap>::AllocateSharedPtr(m_ncoeffs,*m_exp,
                                                                                m_bndCondExpansions,
                                                                                m_bndConditions,
                                                                                periodicVertices,
                                                                                periodicEdges);

	    m_contNcoeffs = m_locToGloMap->GetNumGlobalCoeffs();
	    m_contCoeffs  = Array<OneD,NekDouble>(m_contNcoeffs,0.0);
        }

        ContField2D::ContField2D(SpatialDomains::MeshGraph2D &graph2D,
                                 SpatialDomains::BoundaryConditions &bcs,
                                 const std::string variable):
            ContExpList2D(graph2D,false),
            m_bndCondExpansions(),
            m_bndConditions()
        {
            GenerateBoundaryConditionExpansion(graph2D,bcs,variable);
            EvaluateBoundaryConditions();

            map<int,int> periodicEdges;
            map<int,int> periodicVertices;
            GetPeriodicEdges(graph2D,bcs,variable,periodicVertices,periodicEdges);

            m_locToGloMap = MemoryManager<LocalToGlobalC0ContMap>::AllocateSharedPtr(m_ncoeffs,*m_exp,
                                                                                m_bndCondExpansions,
                                                                                m_bndConditions,
                                                                                periodicVertices,
                                                                                periodicEdges);

	    m_contNcoeffs = m_locToGloMap->GetNumGlobalCoeffs();
	    m_contCoeffs  = Array<OneD,NekDouble>(m_contNcoeffs,0.0);
        }

        ContField2D::ContField2D(const LibUtilities::BasisKey &TriBa,
                                 const LibUtilities::BasisKey &TriBb,
                                 const LibUtilities::BasisKey &QuadBa,
                                 const LibUtilities::BasisKey &QuadBb,
                                 SpatialDomains::MeshGraph2D &graph2D,
                                 SpatialDomains::BoundaryConditions &bcs,
                                 const int bc_loc,
                                 const LibUtilities::PointsType TriNb):
            ContExpList2D(TriBa,TriBb,QuadBa,QuadBb,graph2D,TriNb,false)  ,
            m_bndCondExpansions(),
            m_bndConditions()
        {
            GenerateBoundaryConditionExpansion(graph2D,bcs,bcs.GetVariable(bc_loc));
            EvaluateBoundaryConditions();

            map<int,int> periodicEdges;
            map<int,int> periodicVertices;
            GetPeriodicEdges(graph2D,bcs,bcs.GetVariable(bc_loc),periodicVertices,periodicEdges);

            m_locToGloMap = MemoryManager<LocalToGlobalC0ContMap>::AllocateSharedPtr(m_ncoeffs,*m_exp,
                                                                                m_bndCondExpansions,
                                                                                m_bndConditions,
                                                                                periodicVertices,
                                                                                periodicEdges);

	    m_contNcoeffs = m_locToGloMap->GetNumGlobalCoeffs();
	    m_contCoeffs  = Array<OneD,NekDouble>(m_contNcoeffs,0.0);
        }

        ContField2D::ContField2D(const LibUtilities::BasisKey &TriBa,
                                 const LibUtilities::BasisKey &TriBb,
                                 const LibUtilities::BasisKey &QuadBa,
                                 const LibUtilities::BasisKey &QuadBb,
                                 SpatialDomains::MeshGraph2D &graph2D,
                                 SpatialDomains::BoundaryConditions &bcs,
                                 const std::string variable,
                                 const LibUtilities::PointsType TriNb):
            ContExpList2D(TriBa,TriBb,QuadBa,QuadBb,graph2D,TriNb,false),
            m_bndCondExpansions(),
            m_bndConditions()
        {
            GenerateBoundaryConditionExpansion(graph2D,bcs,variable);
            EvaluateBoundaryConditions();

            map<int,int> periodicEdges;
            map<int,int> periodicVertices;
            GetPeriodicEdges(graph2D,bcs,variable,periodicVertices,periodicEdges);

            m_locToGloMap = MemoryManager<LocalToGlobalC0ContMap>::AllocateSharedPtr(m_ncoeffs,*m_exp,
                                                                                m_bndCondExpansions,
                                                                                m_bndConditions,
                                                                                periodicVertices,
                                                                                periodicEdges);

	    m_contNcoeffs = m_locToGloMap->GetNumGlobalCoeffs();
	    m_contCoeffs  = Array<OneD,NekDouble>(m_contNcoeffs,0.0);
        }

        ContField2D::~ContField2D()
        {
        }

        void ContField2D::GenerateBoundaryConditionExpansion(SpatialDomains::MeshGraph2D &graph2D,
                                                             SpatialDomains::BoundaryConditions &bcs,
                                                             const std::string variable)
        {
            int cnt1  = 0;
            int cnt2  = 0;
            SpatialDomains::BoundaryRegionCollection    &bregions = bcs.GetBoundaryRegions();
            SpatialDomains::BoundaryConditionCollection &bconditions = bcs.GetBoundaryConditions();

            int nbnd = bregions.size();

            // count the number of non-periodic boundary regions
            for(int i = 0; i < nbnd; ++i)
            {
                if( ((*(bconditions[i]))[variable])->GetBoundaryConditionType() != SpatialDomains::ePeriodic )
                {
                    cnt1++;
                    if( ((*(bconditions[i]))[variable])->GetBoundaryConditionType() == SpatialDomains::eDirichlet )
                    {
                        cnt2++;
                    }
                }
            }
            m_numDirBndCondExpansions = cnt2;
            m_bndCondExpansions  = Array<OneD,MultiRegions::ExpList1DSharedPtr>(cnt1);
            m_bndConditions      = Array<OneD,SpatialDomains::BoundaryConditionShPtr>(cnt1);

            SetBoundaryConditionExpansion(graph2D,bcs,variable,m_bndCondExpansions,m_bndConditions);
        }

        void ContField2D::FwdTrans(const ExpList &In)
        {
            // Inner product of forcing
            Array<OneD,NekDouble> rhs(m_contNcoeffs);

            // Inner product of forcing using m_coeffs as workspace
            IProductWRTBase(In.GetPhys(), rhs, m_coeffs);

            // Solve the system
            GlobalLinSysKey key(StdRegions::eMass);
            GlobalSolve(key,rhs,m_contCoeffs);

            m_transState = eContinuous;
            m_physState = false;
        }

        void ContField2D::MultiplyByInvMassMatrix(const Array<OneD, const NekDouble> &inarray,
                                                        Array<OneD,       NekDouble> &outarray,
                                                  bool ContinuousArrays,
                                                  bool ZeroBCs)
        {
            GlobalLinSysKey key(StdRegions::eMass);

            if(ContinuousArrays)
            {
                if(inarray.data() == outarray.data())
                {
                    Array<OneD, NekDouble> tmp(m_contNcoeffs,0.0);
                    Vmath::Vcopy(m_contNcoeffs,inarray,1,tmp,1);
                    GlobalSolve(key,tmp,outarray);
                }
                else
                {
                    GlobalSolve(key,inarray,outarray);
                }
            }
            else
            {
                Array<OneD, NekDouble> globaltmp(m_contNcoeffs,0.0);

                if(inarray.data() == outarray.data())
                {
                    Array<OneD,NekDouble> tmp(inarray.num_elements());
                    Vmath::Vcopy(inarray.num_elements(),inarray,1,tmp,1);
                    Assemble(tmp,outarray);
                }
                else
                {
                    Assemble(inarray,outarray);
                }

                GlobalSolve(key,outarray,globaltmp);
                GlobalToLocal(globaltmp,outarray);
            }
        }

        // Solve the helmholtz problem assuming that m_contCoeff vector
        // contains an intial estimate for solution
        void ContField2D::HelmSolve(const ExpList &In,
                                    NekDouble lambda,
                                    Array<OneD, NekDouble>& dirForcing)
        {
            GlobalLinSysKey key(StdRegions::eHelmholtz,lambda);
            Array<OneD,NekDouble> rhs(m_contNcoeffs);

            // Inner product of forcing  using m_coeffs as work space
            IProductWRTBase(In.GetPhys(), rhs, m_coeffs);

            // Note -1.0 term necessary to invert forcing function to
            // be consistent with matrix definition
            Vmath::Neg(m_contNcoeffs,  rhs, 1);

            GlobalSolve(key,rhs,m_contCoeffs,dirForcing);

            m_transState = eContinuous;
            m_physState = false;
        }

        void  ContField2D::LaplaceSolve(const ExpList &In,
                                        const Array<OneD, Array<OneD,NekDouble> >& variablecoeffs,
                                        NekDouble time,
                                        Array<OneD, NekDouble>& dirForcing)
        {
            GlobalLinSysKey key(StdRegions::eLaplacian,time,variablecoeffs);
            Array<OneD,NekDouble> rhs(m_contNcoeffs);

            // Inner product of forcing  using m_coeffs as tmp space
            IProductWRTBase(In.GetPhys(), rhs, m_coeffs);

            // Note -1.0 term necessary to invert forcing function to
            // be consistent with matrix definition
            Vmath::Neg(m_contNcoeffs,  rhs, 1);

            GlobalSolve(key,rhs,m_contCoeffs);

            m_transState = eContinuous;
            m_physState = false;
        }

        void ContField2D::GenerateDirBndCondForcing(const GlobalLinSysKey &key,
                                                    Array<OneD, NekDouble> &inout,
                                                    Array<OneD, NekDouble> &outarray)
        {
            int bndcnt=0;
            const Array<OneD,const int>& map = m_locToGloMap->GetBndCondCoeffsToGlobalCoeffsMap();

            for(int i = 0; i < m_numDirBndCondExpansions; ++i)
            {
                const Array<OneD,const NekDouble>& coeffs =  m_bndCondExpansions[i]->GetCoeffs();
                for(int j = 0; j < (m_bndCondExpansions[i])->GetNcoeffs(); j++)
                {
                    inout[map[bndcnt++]] = coeffs[j];
                }
            }
            GeneralMatrixOp(key, inout, outarray);
        }

        // Note inout contains initial guess and final output.
        void ContField2D::GlobalSolve(const GlobalLinSysKey &key,
                                      const Array<OneD, const  NekDouble> &rhs,
                                      Array<OneD, NekDouble> &inout,
                                      Array<OneD, NekDouble>& dirForcing)
        {
            int i,j;
            int bndcnt=m_locToGloMap->GetNumLocalDirBndCoeffs();
            int NumDirBcs = m_locToGloMap->GetNumGlobalDirBndCoeffs();
            Array<OneD,NekDouble> fce(m_contNcoeffs);

            if(dirForcing.num_elements())
            {
                // Use the precomputed dirichlet forcing
                Vmath::Vsub(m_contNcoeffs, rhs, 1, dirForcing, 1, fce, 1);
            }
            else
            {
                // Compute the dirichlet forcing
                GenerateDirBndCondForcing(key,inout,fce);
                Vmath::Vsub(m_contNcoeffs, rhs, 1, fce, 1, fce, 1);
            }

            // Forcing function with weak boundary conditions
            for(i = m_numDirBndCondExpansions; i < m_bndCondExpansions.num_elements(); ++i)
            {
                for(j = 0; j < (m_bndCondExpansions[i])->GetNcoeffs(); j++)
                {
                    fce[m_locToGloMap->GetBndCondCoeffsToGlobalCoeffsMap(bndcnt++)] +=
                        (m_bndCondExpansions[i]->GetCoeffs())[j];
                }
            }

            if(m_contNcoeffs - NumDirBcs > 0)
            {
                GlobalLinSysSharedPtr LinSys = GetGlobalLinSys(key);

                Array<OneD,NekDouble> sln(fce+NumDirBcs);
                LinSys->Solve(sln,sln,*m_locToGloMap);
            }

            // Recover solution by adding
            Vmath::Zero(NumDirBcs, fce, 1);
            Vmath::Vadd(m_contNcoeffs, fce, 1, inout, 1, inout, 1);
        }

        GlobalLinSysSharedPtr ContField2D::GetGlobalLinSys(const GlobalLinSysKey &mkey)
        {
            GlobalLinSysSharedPtr glo_matrix;
            GlobalLinSysMap::iterator matrixIter = m_globalMat->find(mkey);

            if(matrixIter == m_globalMat->end())
            {
                glo_matrix = GenGlobalLinSys(mkey,m_locToGloMap);
                (*m_globalMat)[mkey] = glo_matrix;
            }
            else
            {
                glo_matrix = matrixIter->second;
            }

            return glo_matrix;
        }

    } // end of namespace
} //end of namespace
<commit_msg>Added new defintiion of GlobalLinSysKey to include LocalToGlobalBaseMap and removed use of zerobcs from MultiplyByInvMassMatrix<commit_after>///////////////////////////////////////////////////////////////////////////////
//
// File ContField2D.cpp
//
// For more information, please see: http://www.nektar.info
//
// The MIT License
//
// Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
// Department of Aeronautics, Imperial College London (UK), and Scientific
// Computing and Imaging Institute, University of Utah (USA).
//
// License for the specific language governing rights and limitations under
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
// Description: Field definition for 2D domain with boundary conditions
//
///////////////////////////////////////////////////////////////////////////////

#include <MultiRegions/ContField2D.h>

namespace Nektar
{
    namespace MultiRegions
    {

        ContField2D::ContField2D(void):
            ContExpList2D(),
            m_bndCondExpansions(),
            m_bndConditions()
        {
        }

        ContField2D::ContField2D(const ContField2D &In):
            ContExpList2D(In),
            m_bndCondExpansions(In.m_bndCondExpansions),
            m_bndConditions(In.m_bndConditions)
        {
        }

        ContField2D::ContField2D(const ContField2D &In,
                                 SpatialDomains::MeshGraph2D &graph2D,
                                 SpatialDomains::BoundaryConditions &bcs,
                                 const int bc_loc):
            ContExpList2D(In)
        {
            // Set up boundary conditions for this variable.
            GenerateBoundaryConditionExpansion(graph2D,bcs,bcs.GetVariable(bc_loc));
            EvaluateBoundaryConditions();

            if(!SameTypeOfBoundaryConditions(In))
            {
                map<int,int> periodicEdges;
                map<int,int> periodicVertices;
                GetPeriodicEdges(graph2D,bcs,bcs.GetVariable(bc_loc),periodicVertices,periodicEdges);

                m_locToGloMap = MemoryManager<LocalToGlobalC0ContMap>::AllocateSharedPtr(m_ncoeffs,*m_exp, m_bndCondExpansions, m_bndConditions, periodicVertices, periodicEdges);

            }
            else
            {
                m_locToGloMap = In.m_locToGloMap;
            }

            m_contNcoeffs = m_locToGloMap->GetNumGlobalCoeffs();
	    m_contCoeffs  = Array<OneD,NekDouble>(m_contNcoeffs,0.0);
        }

        ContField2D::ContField2D(SpatialDomains::MeshGraph2D &graph2D,
                                 SpatialDomains::BoundaryConditions &bcs,
                                 const int bc_loc):
            ContExpList2D(graph2D,false)  ,
            m_bndCondExpansions(),
            m_bndConditions()
        {
            GenerateBoundaryConditionExpansion(graph2D,bcs,bcs.GetVariable(bc_loc));
            EvaluateBoundaryConditions();

            map<int,int> periodicEdges;
            map<int,int> periodicVertices;
            GetPeriodicEdges(graph2D,bcs,bcs.GetVariable(bc_loc),periodicVertices,periodicEdges);

            m_locToGloMap = MemoryManager<LocalToGlobalC0ContMap>::AllocateSharedPtr(m_ncoeffs,*m_exp, m_bndCondExpansions, m_bndConditions, periodicVertices, periodicEdges);

	    m_contNcoeffs = m_locToGloMap->GetNumGlobalCoeffs();
	    m_contCoeffs  = Array<OneD,NekDouble>(m_contNcoeffs,0.0);
        }

        ContField2D::ContField2D(SpatialDomains::MeshGraph2D &graph2D,
                                 SpatialDomains::BoundaryConditions &bcs,
                                 const std::string variable):
            ContExpList2D(graph2D,false),
            m_bndCondExpansions(),
            m_bndConditions()
        {
            GenerateBoundaryConditionExpansion(graph2D,bcs,variable);
            EvaluateBoundaryConditions();

            map<int,int> periodicEdges;
            map<int,int> periodicVertices;
            GetPeriodicEdges(graph2D,bcs,variable,periodicVertices,periodicEdges);

            m_locToGloMap = MemoryManager<LocalToGlobalC0ContMap>::AllocateSharedPtr(m_ncoeffs,*m_exp,
                                                                                m_bndCondExpansions,
                                                                                m_bndConditions,
                                                                                periodicVertices,
                                                                                periodicEdges);

	    m_contNcoeffs = m_locToGloMap->GetNumGlobalCoeffs();
	    m_contCoeffs  = Array<OneD,NekDouble>(m_contNcoeffs,0.0);
        }

        ContField2D::ContField2D(const LibUtilities::BasisKey &TriBa,
                                 const LibUtilities::BasisKey &TriBb,
                                 const LibUtilities::BasisKey &QuadBa,
                                 const LibUtilities::BasisKey &QuadBb,
                                 SpatialDomains::MeshGraph2D &graph2D,
                                 SpatialDomains::BoundaryConditions &bcs,
                                 const int bc_loc,
                                 const LibUtilities::PointsType TriNb):
            ContExpList2D(TriBa,TriBb,QuadBa,QuadBb,graph2D,TriNb,false)  ,
            m_bndCondExpansions(),
            m_bndConditions()
        {
            GenerateBoundaryConditionExpansion(graph2D,bcs,bcs.GetVariable(bc_loc));
            EvaluateBoundaryConditions();

            map<int,int> periodicEdges;
            map<int,int> periodicVertices;
            GetPeriodicEdges(graph2D,bcs,bcs.GetVariable(bc_loc),periodicVertices,periodicEdges);

            m_locToGloMap = MemoryManager<LocalToGlobalC0ContMap>::AllocateSharedPtr(m_ncoeffs,*m_exp,
                                                                                m_bndCondExpansions,
                                                                                m_bndConditions,
                                                                                periodicVertices,
                                                                                periodicEdges);

	    m_contNcoeffs = m_locToGloMap->GetNumGlobalCoeffs();
	    m_contCoeffs  = Array<OneD,NekDouble>(m_contNcoeffs,0.0);
        }

        ContField2D::ContField2D(const LibUtilities::BasisKey &TriBa,
                                 const LibUtilities::BasisKey &TriBb,
                                 const LibUtilities::BasisKey &QuadBa,
                                 const LibUtilities::BasisKey &QuadBb,
                                 SpatialDomains::MeshGraph2D &graph2D,
                                 SpatialDomains::BoundaryConditions &bcs,
                                 const std::string variable,
                                 const LibUtilities::PointsType TriNb):
            ContExpList2D(TriBa,TriBb,QuadBa,QuadBb,graph2D,TriNb,false),
            m_bndCondExpansions(),
            m_bndConditions()
        {
            GenerateBoundaryConditionExpansion(graph2D,bcs,variable);
            EvaluateBoundaryConditions();

            map<int,int> periodicEdges;
            map<int,int> periodicVertices;
            GetPeriodicEdges(graph2D,bcs,variable,periodicVertices,periodicEdges);

            m_locToGloMap = MemoryManager<LocalToGlobalC0ContMap>::AllocateSharedPtr(m_ncoeffs,*m_exp,
                                                                                m_bndCondExpansions,
                                                                                m_bndConditions,
                                                                                periodicVertices,
                                                                                periodicEdges);

	    m_contNcoeffs = m_locToGloMap->GetNumGlobalCoeffs();
	    m_contCoeffs  = Array<OneD,NekDouble>(m_contNcoeffs,0.0);
        }

        ContField2D::~ContField2D()
        {
        }

        bool ContField2D::SameTypeOfBoundaryConditions(const ContField2D &In)
        {
            int i;
            bool returnval = true;

            for(i = 0; i < m_bndConditions.num_elements(); ++i)
            {

                // check to see if boundary condition type is the same
                // and there are the same number of boundary
                // conditions in the boundary definition.
                if((m_bndConditions[i]->GetBoundaryConditionType()
                    != In.m_bndConditions[i]->GetBoundaryConditionType())||
                   (m_bndCondExpansions[i]->GetExpSize() != In.m_bndCondExpansions[i]->GetExpSize()))
                {
                    returnval = false;
                    break;
                }
            }

            return returnval;
        }

        void ContField2D::GenerateBoundaryConditionExpansion(SpatialDomains::MeshGraph2D &graph2D,
                                                             SpatialDomains::BoundaryConditions &bcs,
                                                             const std::string variable)
        {
            int cnt1  = 0;
            int cnt2  = 0;
            SpatialDomains::BoundaryRegionCollection    &bregions = bcs.GetBoundaryRegions();
            SpatialDomains::BoundaryConditionCollection &bconditions = bcs.GetBoundaryConditions();

            int nbnd = bregions.size();

            // count the number of non-periodic boundary regions
            for(int i = 0; i < nbnd; ++i)
            {
                if( ((*(bconditions[i]))[variable])->GetBoundaryConditionType() != SpatialDomains::ePeriodic )
                {
                    cnt1++;
                    if( ((*(bconditions[i]))[variable])->GetBoundaryConditionType() == SpatialDomains::eDirichlet )
                    {
                        cnt2++;
                    }
                }
            }
            m_numDirBndCondExpansions = cnt2;
            m_bndCondExpansions = Array<OneD,MultiRegions::ExpList1DSharedPtr>(cnt1);
            m_bndConditions     = Array<OneD,SpatialDomains::BoundaryConditionShPtr>(cnt1);

            SetBoundaryConditionExpansion(graph2D,bcs,variable,m_bndCondExpansions,m_bndConditions);
        }

        void ContField2D::FwdTrans(const ExpList &In)
        {
            ASSERTL1(In.GetPhysState() == true,
                     "Error input state is not in physical  space");

            // Inner product of forcing
            Array<OneD,NekDouble> rhs(m_contNcoeffs);

            // Inner product of forcing using m_coeffs as workspace
            IProductWRTBase(In.GetPhys(), rhs, m_coeffs);

            // Solve the system
            GlobalLinSysKey key(StdRegions::eMass,m_locToGloMap);
            GlobalSolve(key,rhs,m_contCoeffs);

            m_transState = eContinuous;
            m_physState = false;
        }

        void ContField2D::MultiplyByInvMassMatrix(const Array<OneD, const NekDouble> &inarray,
                                                        Array<OneD,       NekDouble> &outarray,
                                                  bool ContinuousArrays)

        {
            GlobalLinSysKey key(StdRegions::eMass,m_locToGloMap);

            if(ContinuousArrays)
            {
                if(inarray.data() == outarray.data())
                {
                    Array<OneD, NekDouble> tmp(m_contNcoeffs,0.0);
                    Vmath::Vcopy(m_contNcoeffs,inarray,1,tmp,1);
                    GlobalSolve(key,tmp,outarray);
                }
                else
                {
                    GlobalSolve(key,inarray,outarray);
                }
            }
            else
            {
                Array<OneD, NekDouble> globaltmp(m_contNcoeffs,0.0);

                if(inarray.data() == outarray.data())
                {
                    Array<OneD,NekDouble> tmp(inarray.num_elements());
                    Vmath::Vcopy(inarray.num_elements(),inarray,1,tmp,1);
                    Assemble(tmp,outarray);
                }
                else
                {
                    Assemble(inarray,outarray);
                }

                GlobalSolve(key,outarray,globaltmp);
                GlobalToLocal(globaltmp,outarray);
            }
        }

        // Solve the helmholtz problem assuming that m_contCoeff vector
        // contains an intial estimate for solution
        void ContField2D::HelmSolve(const ExpList &In,
                                    NekDouble lambda,
                                    Array<OneD, NekDouble>& dirForcing)
        {
            GlobalLinSysKey key(StdRegions::eHelmholtz,m_locToGloMap,lambda);
            Array<OneD,NekDouble> rhs(m_contNcoeffs);

            // Inner product of forcing  using m_coeffs as work space
            IProductWRTBase(In.GetPhys(), rhs, m_coeffs);

            // Note -1.0 term necessary to invert forcing function to
            // be consistent with matrix definition
            Vmath::Neg(m_contNcoeffs,  rhs, 1);

            GlobalSolve(key,rhs,m_contCoeffs,dirForcing);

            m_transState = eContinuous;
            m_physState = false;
        }

        void  ContField2D::LaplaceSolve(const ExpList &In,
                                        const Array<OneD, Array<OneD,NekDouble> >& variablecoeffs,
                                        NekDouble time,
                                        Array<OneD, NekDouble>& dirForcing)
        {
            GlobalLinSysKey key(StdRegions::eLaplacian,m_locToGloMap,time,variablecoeffs);
            Array<OneD,NekDouble> rhs(m_contNcoeffs);

            // Inner product of forcing  using m_coeffs as tmp space
            IProductWRTBase(In.GetPhys(), rhs, m_coeffs);

            // Note -1.0 term necessary to invert forcing function to
            // be consistent with matrix definition
            Vmath::Neg(m_contNcoeffs,  rhs, 1);

            GlobalSolve(key,rhs,m_contCoeffs);

            m_transState = eContinuous;
            m_physState = false;
        }

        void ContField2D::GenerateDirBndCondForcing(const GlobalLinSysKey &key,
                                                    Array<OneD, NekDouble> &inout,
                                                    Array<OneD, NekDouble> &outarray)
        {
            int bndcnt=0;
            const Array<OneD,const int>& map = m_locToGloMap->GetBndCondCoeffsToGlobalCoeffsMap();

            for(int i = 0; i < m_numDirBndCondExpansions; ++i)
            {
                const Array<OneD,const NekDouble>& coeffs = m_bndCondExpansions[i]->GetCoeffs();
                for(int j = 0; j < (m_bndCondExpansions[i])->GetNcoeffs(); ++j)
                {
                    inout[map[bndcnt++]] = coeffs[j];
                }
            }
            GeneralMatrixOp(key, inout, outarray);
        }

        // Note inout contains initial guess and final output.
        void ContField2D::GlobalSolve(const GlobalLinSysKey &key,
                                      const Array<OneD, const  NekDouble> &rhs,
                                      Array<OneD, NekDouble> &inout,
                                      Array<OneD, NekDouble>& dirForcing)
        {
            int i,j;
            int bndcnt=m_locToGloMap->GetNumLocalDirBndCoeffs();
            int NumDirBcs = m_locToGloMap->GetNumGlobalDirBndCoeffs();
            Array<OneD,NekDouble> fce(m_contNcoeffs);

            if(dirForcing.num_elements())
            {
                // Use the precomputed dirichlet forcing
                Vmath::Vsub(m_contNcoeffs, rhs, 1, dirForcing, 1, fce, 1);
            }
            else
            {
                // Compute the dirichlet forcing
                GenerateDirBndCondForcing(key,inout,fce);
                Vmath::Vsub(m_contNcoeffs, rhs, 1, fce, 1, fce, 1);
            }

            // Forcing function with weak boundary conditions
            for(i = m_numDirBndCondExpansions; i < m_bndCondExpansions.num_elements(); ++i)
            {
                for(j = 0; j < (m_bndCondExpansions[i])->GetNcoeffs(); j++)
                {
                    fce[m_locToGloMap->GetBndCondCoeffsToGlobalCoeffsMap(bndcnt++)] +=
                        (m_bndCondExpansions[i]->GetCoeffs())[j];
                }
            }

            if(m_contNcoeffs - NumDirBcs > 0)
            {
                GlobalLinSysSharedPtr LinSys = GetGlobalLinSys(key);

                Array<OneD,NekDouble> sln(fce+NumDirBcs);
                LinSys->Solve(sln,sln,*m_locToGloMap);
            }

            // Recover solution by adding
            Vmath::Zero(NumDirBcs, fce, 1);
            Vmath::Vadd(m_contNcoeffs, fce, 1, inout, 1, inout, 1);
        }

        GlobalLinSysSharedPtr ContField2D::GetGlobalLinSys(const GlobalLinSysKey &mkey)
        {
            ASSERTL1(mkey.LocToGloMapIsDefined(),
                     "To use method must have a LocalToGlobalBaseMap "
                     "attached to key");

            GlobalLinSysSharedPtr glo_matrix;
            GlobalLinSysMap::iterator matrixIter = m_globalMat->find(mkey);

            if(matrixIter == m_globalMat->end())
            {
                glo_matrix = GenGlobalLinSys(mkey,m_locToGloMap);
                (*m_globalMat)[mkey] = glo_matrix;
            }
            else
            {
                glo_matrix = matrixIter->second;
            }

            return glo_matrix;
        }

    } // end of namespace
} //end of namespace
<|endoftext|>


<commit_before>#include <iostream>

#include "TH1D.h"
#include "TH1F.h"
#include "TMath.h"

#include "PlotBase.h"
#include "HistAnalysis.h"

ClassImp(HistAnalysis)

//--------------------------------------------------------------------
void
HistAnalysis::DoScaleFactors(TString PlotVar, Int_t NumBins, Double_t *XBins,
                             ScaleFactorMethod  method, Bool_t NormalizeBackground,
                             TString TreeName)
{
  SetDefaultExpr(PlotVar);

  // First create the data histograms
  SetDefaultTree(ReturnTChain(TreeName,kData));
  AddWeight(fBaseCut);
  for (UInt_t iCut = 0; iCut != fScaleFactorCuts.size(); ++iCut)
    AddWeight(fBaseCut + " && " + fScaleFactorCuts[iCut]);
  std::vector<TH1D*> dataHists = MakeHists(NumBins,XBins);

  // Then create the signal histograms
  SetDefaultTree(ReturnTChain(TreeName,fSignalType,fSignalName,kLegendEntry));
  ResetWeight();
  AddWeight(TString("(") + fBaseCut + ")*(" + fMCWeight + ")");
  for (UInt_t iCut = 0; iCut != fScaleFactorCuts.size(); ++iCut)
    AddWeight(TString("(") + fBaseCut + " && " + fScaleFactorCuts[iCut] + ")*(" + fMCWeight + ")");
  std::vector<TH1D*> signalHists = MakeHists(NumBins,XBins);

  // Then create the background histograms
  SetDefaultTree(ReturnTChain(TreeName,kBackground,fSignalName,kLegendEntry,false));
  std::vector<TH1D*> backgroundHists = MakeHists(NumBins,XBins);

  // Subtract out the background
  Double_t scale = -1.0;
  if (NormalizeBackground)
    scale *= dataHists[0]->Integral()/(backgroundHists[0]->Integral() + signalHists[0]->Integral());

  for (UInt_t iHist = 0; iHist != dataHists.size(); ++iHist)
    dataHists[iHist]->Add(backgroundHists[iHist],scale);

  std::vector<Double_t> data_yields;
  std::vector<Double_t> data_error;
  std::vector<Double_t> mc_yields;
  std::vector<Double_t> mc_error;

  Double_t factor = 1.0;

  switch (method) {

  case kCutAndCount:

    // Get the yields from each histogram
    for (UInt_t iHist = 0; iHist != dataHists.size(); ++iHist) {
      Double_t error = 0.0;
      Double_t yield = dataHists[iHist]->IntegralAndError(1,NumBins,error);
      data_yields.push_back(yield);
      data_error.push_back(error);
      error = 0.0;
      yield = signalHists[iHist]->IntegralAndError(1,NumBins,error);
      mc_yields.push_back(yield);
      mc_error.push_back(error);
    }

    factor = mc_yields[0]/data_yields[0];

    break;

  default:
    std::cout << "I do not support that option right now." << std::endl;
    break;
  }

  std::cout << "\\hline" << std::endl;
  std::cout << " & No Cut";
  for (UInt_t iCut = 0; iCut != fCutNames.size(); ++iCut)
    std::cout << " & " << fCutNames[iCut];
  std::cout << " \\\\" << std::endl;
  std::cout << "\\hline" << std::endl;

  std::cout << "Background Subtracted Data";
  for (UInt_t iYield = 0; iYield != data_yields.size(); ++iYield) {
    std::cout << " & " << TString::Format(fFormat,data_yields[iYield]);
    std::cout << " \\pm " << TString::Format(fFormat,data_error[iYield]);
  }
  std::cout << " \\\\" << std::endl;

  std::cout << "Truth-matched MC";
  for (UInt_t iYield = 0; iYield != mc_yields.size(); ++iYield) {
    std::cout << " & " << TString::Format(fFormat,mc_yields[iYield]);
    std::cout << " \\pm " << TString::Format(fFormat,mc_error[iYield]);
  }
  std::cout << " \\\\" << std::endl;
  std::cout << "\\hline" << std::endl;

  std::cout << "Normalized Ratio";
  for (UInt_t iYield = 0; iYield != data_yields.size(); ++iYield) {
    std::cout << " & " << TString::Format(fFormat,data_yields[iYield]/mc_yields[iYield] * factor);
    std::cout << " \\pm " << TString::Format(fFormat,
                                             TMath::Sqrt(pow(data_error[iYield]/mc_yields[iYield],2) +
                                                         pow(data_yields[iYield]/pow(mc_yields[iYield],2) * mc_error[iYield],2)) *
                                             factor);
  }
  std::cout << " \\\\" << std::endl;
  std::cout << "\\hline" << std::endl;

}

//--------------------------------------------------------------------
void
HistAnalysis::DoScaleFactors(TString PlotVar, Int_t NumBins, Double_t MinX, Double_t MaxX,
                             ScaleFactorMethod  method, Bool_t NormalizeBackground,
                             TString TreeName)
{
  Double_t XBins[NumBins+1];
  ConvertToArray(NumBins,MinX,MaxX,XBins);
  DoScaleFactors(PlotVar,NumBins,XBins,method,NormalizeBackground,TreeName);
}

//--------------------------------------------------------------------
void
HistAnalysis::MakeReweightHist(TString OutFile, TString OutHist, TString PlotVar, Int_t NumBins, Double_t *XBins, TString TreeName)
{
  // First create the data histogram
  TH1F *dataHist = new TH1F("dataHist","dataHist",NumBins,XBins);
  dataHist->Sumw2();
  ReturnTChain(TreeName,kData)->Draw(PlotVar + ">>dataHist",fBaseCut);

  // Then create the mc histogram
  TH1F *mcHist = new TH1F("mcHist","mcHist",NumBins,XBins);
  mcHist->Sumw2();
  ReturnTChain(TreeName,kBackground)->Draw(PlotVar + ">>mcHist",TString("(") + fBaseCut + ")*(" + fMCWeight + ")");

  dataHist->Scale(1.0/dataHist->Integral());
  mcHist->Scale(1.0/mcHist->Integral());

  dataHist->Divide(mcHist);

  TFile *theFile = new TFile(OutFile,"RECREATE");
  theFile->WriteTObject(dataHist,OutHist);
}

//--------------------------------------------------------------------
void
HistAnalysis::MakeReweightHist(TString OutFile, TString OutHist, TString PlotVar, Int_t NumBins, Double_t MinX, Double_t MaxX, TString TreeName)
{
  Double_t XBins[NumBins+1];
  ConvertToArray(NumBins,MinX,MaxX,XBins);
  DoScaleFactors(OutFile,OutHist,PlotVar,NumBins,XBins,TreeName);
}
<commit_msg>Ha, now it compiles.<commit_after>#include <iostream>

#include "TH1D.h"
#include "TH1F.h"
#include "TMath.h"

#include "PlotBase.h"
#include "HistAnalysis.h"

ClassImp(HistAnalysis)

//--------------------------------------------------------------------
void
HistAnalysis::DoScaleFactors(TString PlotVar, Int_t NumBins, Double_t *XBins,
                             ScaleFactorMethod  method, Bool_t NormalizeBackground,
                             TString TreeName)
{
  SetDefaultExpr(PlotVar);

  // First create the data histograms
  SetDefaultTree(ReturnTChain(TreeName,kData));
  AddWeight(fBaseCut);
  for (UInt_t iCut = 0; iCut != fScaleFactorCuts.size(); ++iCut)
    AddWeight(fBaseCut + " && " + fScaleFactorCuts[iCut]);
  std::vector<TH1D*> dataHists = MakeHists(NumBins,XBins);

  // Then create the signal histograms
  SetDefaultTree(ReturnTChain(TreeName,fSignalType,fSignalName,kLegendEntry));
  ResetWeight();
  AddWeight(TString("(") + fBaseCut + ")*(" + fMCWeight + ")");
  for (UInt_t iCut = 0; iCut != fScaleFactorCuts.size(); ++iCut)
    AddWeight(TString("(") + fBaseCut + " && " + fScaleFactorCuts[iCut] + ")*(" + fMCWeight + ")");
  std::vector<TH1D*> signalHists = MakeHists(NumBins,XBins);

  // Then create the background histograms
  SetDefaultTree(ReturnTChain(TreeName,kBackground,fSignalName,kLegendEntry,false));
  std::vector<TH1D*> backgroundHists = MakeHists(NumBins,XBins);

  // Subtract out the background
  Double_t scale = -1.0;
  if (NormalizeBackground)
    scale *= dataHists[0]->Integral()/(backgroundHists[0]->Integral() + signalHists[0]->Integral());

  for (UInt_t iHist = 0; iHist != dataHists.size(); ++iHist)
    dataHists[iHist]->Add(backgroundHists[iHist],scale);

  std::vector<Double_t> data_yields;
  std::vector<Double_t> data_error;
  std::vector<Double_t> mc_yields;
  std::vector<Double_t> mc_error;

  Double_t factor = 1.0;

  switch (method) {

  case kCutAndCount:

    // Get the yields from each histogram
    for (UInt_t iHist = 0; iHist != dataHists.size(); ++iHist) {
      Double_t error = 0.0;
      Double_t yield = dataHists[iHist]->IntegralAndError(1,NumBins,error);
      data_yields.push_back(yield);
      data_error.push_back(error);
      error = 0.0;
      yield = signalHists[iHist]->IntegralAndError(1,NumBins,error);
      mc_yields.push_back(yield);
      mc_error.push_back(error);
    }

    factor = mc_yields[0]/data_yields[0];

    break;

  default:
    std::cout << "I do not support that option right now." << std::endl;
    break;
  }

  std::cout << "\\hline" << std::endl;
  std::cout << " & No Cut";
  for (UInt_t iCut = 0; iCut != fCutNames.size(); ++iCut)
    std::cout << " & " << fCutNames[iCut];
  std::cout << " \\\\" << std::endl;
  std::cout << "\\hline" << std::endl;

  std::cout << "Background Subtracted Data";
  for (UInt_t iYield = 0; iYield != data_yields.size(); ++iYield) {
    std::cout << " & " << TString::Format(fFormat,data_yields[iYield]);
    std::cout << " \\pm " << TString::Format(fFormat,data_error[iYield]);
  }
  std::cout << " \\\\" << std::endl;

  std::cout << "Truth-matched MC";
  for (UInt_t iYield = 0; iYield != mc_yields.size(); ++iYield) {
    std::cout << " & " << TString::Format(fFormat,mc_yields[iYield]);
    std::cout << " \\pm " << TString::Format(fFormat,mc_error[iYield]);
  }
  std::cout << " \\\\" << std::endl;
  std::cout << "\\hline" << std::endl;

  std::cout << "Normalized Ratio";
  for (UInt_t iYield = 0; iYield != data_yields.size(); ++iYield) {
    std::cout << " & " << TString::Format(fFormat,data_yields[iYield]/mc_yields[iYield] * factor);
    std::cout << " \\pm " << TString::Format(fFormat,
                                             TMath::Sqrt(pow(data_error[iYield]/mc_yields[iYield],2) +
                                                         pow(data_yields[iYield]/pow(mc_yields[iYield],2) * mc_error[iYield],2)) *
                                             factor);
  }
  std::cout << " \\\\" << std::endl;
  std::cout << "\\hline" << std::endl;

}

//--------------------------------------------------------------------
void
HistAnalysis::DoScaleFactors(TString PlotVar, Int_t NumBins, Double_t MinX, Double_t MaxX,
                             ScaleFactorMethod  method, Bool_t NormalizeBackground,
                             TString TreeName)
{
  Double_t XBins[NumBins+1];
  ConvertToArray(NumBins,MinX,MaxX,XBins);
  DoScaleFactors(PlotVar,NumBins,XBins,method,NormalizeBackground,TreeName);
}

//--------------------------------------------------------------------
void
HistAnalysis::MakeReweightHist(TString OutFile, TString OutHist, TString PlotVar, Int_t NumBins, Double_t *XBins, TString TreeName)
{
  // First create the data histogram
  TH1F *dataHist = new TH1F("dataHist","dataHist",NumBins,XBins);
  dataHist->Sumw2();
  ReturnTChain(TreeName,kData)->Draw(PlotVar + ">>dataHist",fBaseCut);

  // Then create the mc histogram
  TH1F *mcHist = new TH1F("mcHist","mcHist",NumBins,XBins);
  mcHist->Sumw2();
  ReturnTChain(TreeName,kBackground)->Draw(PlotVar + ">>mcHist",TString("(") + fBaseCut + ")*(" + fMCWeight + ")");

  dataHist->Scale(1.0/dataHist->Integral());
  mcHist->Scale(1.0/mcHist->Integral());

  dataHist->Divide(mcHist);

  TFile *theFile = new TFile(OutFile,"RECREATE");
  theFile->WriteTObject(dataHist,OutHist);
}

//--------------------------------------------------------------------
void
HistAnalysis::MakeReweightHist(TString OutFile, TString OutHist, TString PlotVar, Int_t NumBins, Double_t MinX, Double_t MaxX, TString TreeName)
{
  Double_t XBins[NumBins+1];
  ConvertToArray(NumBins,MinX,MaxX,XBins);
  MakeReweightHist(OutFile,OutHist,PlotVar,NumBins,XBins,TreeName);
}
<|endoftext|>