<commit_before>/*
 * #%L
 * %%
 * Copyright (C) 2011 - 2017 BMW Car IT GmbH
 * %%
 * 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.
 * #L%
 */

#include <chrono>
#include <functional>
#include <map>
#include <memory>
#include <vector>

#include <gtest/gtest.h>

#include "joynr/LocalDiscoveryAggregator.h"

#include "joynr/Future.h"
#include "joynr/Semaphore.h"
#include "joynr/types/DiscoveryEntryWithMetaInfo.h"
#include "joynr/types/DiscoveryQos.h"

#include "tests/mock/MockDiscovery.h"

using namespace ::testing;
using namespace joynr;

class LocalDiscoveryAggregatorTest : public ::testing::Test
{
public:
    LocalDiscoveryAggregatorTest()
            : provisionedDiscoveryEntries(),
              localDiscoveryAggregator(provisionedDiscoveryEntries),
              discoveryMock(std::make_shared<MockDiscovery>())
    {
    }

    ~LocalDiscoveryAggregatorTest() override
    {
    }

protected:
    std::map<std::string, types::DiscoveryEntryWithMetaInfo> provisionedDiscoveryEntries;
    LocalDiscoveryAggregator localDiscoveryAggregator;
    std::shared_ptr<MockDiscovery> discoveryMock;

private:
    DISALLOW_COPY_AND_ASSIGN(LocalDiscoveryAggregatorTest);
};

TEST_F(LocalDiscoveryAggregatorTest, addAsync_proxyNotSet_doesNotThrow)
{
    const types::DiscoveryEntryWithMetaInfo discoveryEntry;
    EXPECT_DEATH(localDiscoveryAggregator.addAsync(discoveryEntry, false, nullptr, nullptr),
                 "Assertion.*");
}

TEST_F(LocalDiscoveryAggregatorTest, lookupAsyncDomainInterface_proxyNotSet_doesNotThrow)
{
    const std::vector<std::string> domains;
    const std::string interfaceName;
    const types::DiscoveryQos discoveryQos;
    EXPECT_DEATH(localDiscoveryAggregator.lookupAsync(
                         domains, interfaceName, discoveryQos),
                 "Assertion.*");
}

TEST_F(LocalDiscoveryAggregatorTest, lookupAsyncParticipantId_proxyNotSet_doesNotThrow)
{
    const std::string participantId;
    EXPECT_DEATH(
            localDiscoveryAggregator.lookupAsync(participantId), "Assertion.*");
}

TEST_F(LocalDiscoveryAggregatorTest, removeAsync_proxyNotSet_doesNotThrow)
{
    const std::string participantId;
    EXPECT_DEATH(
            localDiscoveryAggregator.removeAsync(participantId), "Assertion.*");
}

TEST_F(LocalDiscoveryAggregatorTest, addAsync_callsProxy)
{
    localDiscoveryAggregator.setDiscoveryProxy(discoveryMock);

    types::DiscoveryEntryWithMetaInfo discoveryEntry;
    discoveryEntry.setParticipantId("testParticipantId");
    EXPECT_CALL(*discoveryMock, addAsyncMock(Eq(discoveryEntry), _, Eq(nullptr), Eq(nullptr), _));
    localDiscoveryAggregator.addAsync(discoveryEntry, false, nullptr, nullptr);
}

TEST_F(LocalDiscoveryAggregatorTest, lookupAsyncDomainInterface_callsProxy)
{
    localDiscoveryAggregator.setDiscoveryProxy(discoveryMock);

    const std::vector<std::string> domains{"domain1", "domain2"};
    const std::string interfaceName("testInterfaceName");
    types::DiscoveryQos discoveryQos;
    discoveryQos.setDiscoveryTimeout(42421);
    EXPECT_CALL(
            *discoveryMock,
            lookupAsyncMock(
                    Eq(domains), Eq(interfaceName), Eq(discoveryQos),Matcher<std::function<void(const std::vector<joynr::types::DiscoveryEntryWithMetaInfo>& result)>>(_),_,_));
    localDiscoveryAggregator.lookupAsync(domains, interfaceName, discoveryQos);
}

TEST_F(LocalDiscoveryAggregatorTest, lookupAsyncParticipantId_callsProxy)
{
    localDiscoveryAggregator.setDiscoveryProxy(discoveryMock);

    const std::string participantId("testParticipantId");
    EXPECT_CALL(*discoveryMock, lookupAsyncMock(Eq(participantId), Eq(nullptr), Eq(nullptr), _));
    localDiscoveryAggregator.lookupAsync(participantId);
}

TEST_F(LocalDiscoveryAggregatorTest, removeAsync_callsProxy)
{
    localDiscoveryAggregator.setDiscoveryProxy(discoveryMock);

    const std::string participantId("testParticipantId");
    EXPECT_CALL(*discoveryMock, removeAsyncMock(Eq(participantId), Eq(nullptr), Eq(nullptr), _));
    localDiscoveryAggregator.removeAsync(participantId, nullptr, nullptr);
}

TEST_F(LocalDiscoveryAggregatorTest, lookupAsyncParticipantId_provisionedEntry_doesNotCallProxy)
{
    Semaphore semaphore(0);
    const std::string participantId("testProvisionedParticipantId");

    types::DiscoveryEntryWithMetaInfo provisionedDiscoveryEntry;
    provisionedDiscoveryEntry.setParticipantId("testProvisionedParticipantId");
    provisionedDiscoveryEntries.insert(std::make_pair(participantId, provisionedDiscoveryEntry));
    LocalDiscoveryAggregator localDiscoveryAggregator(provisionedDiscoveryEntries);
    localDiscoveryAggregator.setDiscoveryProxy(discoveryMock);

    EXPECT_CALL(*discoveryMock, lookupAsyncMock(_, _, _, _)).Times(0);

    auto onSuccess = [&semaphore, &provisionedDiscoveryEntry](
            const types::DiscoveryEntryWithMetaInfo& entry) {
        EXPECT_EQ(entry, provisionedDiscoveryEntry);
        semaphore.notify();
    };

    auto future = localDiscoveryAggregator.lookupAsync(participantId, onSuccess, nullptr);

    types::DiscoveryEntryWithMetaInfo result;
    future->get(100, result);

    EXPECT_EQ(result, provisionedDiscoveryEntry);

    EXPECT_TRUE(semaphore.waitFor(std::chrono::milliseconds(100)));
}
<commit_msg>[C++] Added addAsync test with gbids to LocalDiscoveryAggregatorTest<commit_after>/*
 * #%L
 * %%
 * Copyright (C) 2011 - 2017 BMW Car IT GmbH
 * %%
 * 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.
 * #L%
 */

#include <chrono>
#include <functional>
#include <map>
#include <memory>
#include <vector>

#include <gtest/gtest.h>

#include "joynr/LocalDiscoveryAggregator.h"

#include "joynr/Future.h"
#include "joynr/Semaphore.h"
#include "joynr/types/DiscoveryEntryWithMetaInfo.h"
#include "joynr/types/DiscoveryError.h"
#include "joynr/types/DiscoveryQos.h"

#include "tests/mock/MockDiscovery.h"

using ::testing::DoAll;

using namespace ::testing;
using namespace joynr;

class LocalDiscoveryAggregatorTest : public ::testing::Test
{
public:
    LocalDiscoveryAggregatorTest()
            : provisionedDiscoveryEntries(),
              localDiscoveryAggregator(provisionedDiscoveryEntries),
              discoveryMock(std::make_shared<MockDiscovery>())
    {
    }

    ~LocalDiscoveryAggregatorTest() override
    {
    }

protected:
    std::map<std::string, types::DiscoveryEntryWithMetaInfo> provisionedDiscoveryEntries;
    LocalDiscoveryAggregator localDiscoveryAggregator;
    std::shared_ptr<MockDiscovery> discoveryMock;

private:
    DISALLOW_COPY_AND_ASSIGN(LocalDiscoveryAggregatorTest);
};

TEST_F(LocalDiscoveryAggregatorTest, addAsync_proxyNotSet_doesNotThrow)
{
    const types::DiscoveryEntryWithMetaInfo discoveryEntry;
    EXPECT_DEATH(localDiscoveryAggregator.addAsync(discoveryEntry, false, nullptr, nullptr),
                 "Assertion.*");
}

TEST_F(LocalDiscoveryAggregatorTest, lookupAsyncDomainInterface_proxyNotSet_doesNotThrow)
{
    const std::vector<std::string> domains;
    const std::string interfaceName;
    const types::DiscoveryQos discoveryQos;
    EXPECT_DEATH(localDiscoveryAggregator.lookupAsync(
                         domains, interfaceName, discoveryQos),
                 "Assertion.*");
}

TEST_F(LocalDiscoveryAggregatorTest, lookupAsyncParticipantId_proxyNotSet_doesNotThrow)
{
    const std::string participantId;
    EXPECT_DEATH(
            localDiscoveryAggregator.lookupAsync(participantId), "Assertion.*");
}

TEST_F(LocalDiscoveryAggregatorTest, removeAsync_proxyNotSet_doesNotThrow)
{
    const std::string participantId;
    EXPECT_DEATH(
            localDiscoveryAggregator.removeAsync(participantId), "Assertion.*");
}

TEST_F(LocalDiscoveryAggregatorTest, addAsync_callsProxy)
{
    localDiscoveryAggregator.setDiscoveryProxy(discoveryMock);

    types::DiscoveryEntryWithMetaInfo discoveryEntry;
    discoveryEntry.setParticipantId("testParticipantId");
    EXPECT_CALL(*discoveryMock, addAsyncMock(Eq(discoveryEntry), _, Eq(nullptr), Eq(nullptr), _));
    localDiscoveryAggregator.addAsync(discoveryEntry, false, nullptr, nullptr);
}

TEST_F(LocalDiscoveryAggregatorTest, lookupAsyncDomainInterface_callsProxy)
{
    localDiscoveryAggregator.setDiscoveryProxy(discoveryMock);

    const std::vector<std::string> domains{"domain1", "domain2"};
    const std::string interfaceName("testInterfaceName");
    types::DiscoveryQos discoveryQos;
    discoveryQos.setDiscoveryTimeout(42421);
    EXPECT_CALL(
            *discoveryMock,
            lookupAsyncMock(
                    Eq(domains), Eq(interfaceName), Eq(discoveryQos),Matcher<std::function<void(const std::vector<joynr::types::DiscoveryEntryWithMetaInfo>& result)>>(_),_,_));
    localDiscoveryAggregator.lookupAsync(domains, interfaceName, discoveryQos);
}

TEST_F(LocalDiscoveryAggregatorTest, lookupAsyncParticipantId_callsProxy)
{
    localDiscoveryAggregator.setDiscoveryProxy(discoveryMock);

    const std::string participantId("testParticipantId");
    EXPECT_CALL(*discoveryMock, lookupAsyncMock(Eq(participantId), Eq(nullptr), Eq(nullptr), _));
    localDiscoveryAggregator.lookupAsync(participantId);
}

TEST_F(LocalDiscoveryAggregatorTest, removeAsync_callsProxy)
{
    localDiscoveryAggregator.setDiscoveryProxy(discoveryMock);

    const std::string participantId("testParticipantId");
    EXPECT_CALL(*discoveryMock, removeAsyncMock(Eq(participantId), Eq(nullptr), Eq(nullptr), _));
    localDiscoveryAggregator.removeAsync(participantId, nullptr, nullptr);
}

TEST_F(LocalDiscoveryAggregatorTest, lookupAsyncParticipantId_provisionedEntry_doesNotCallProxy)
{
    Semaphore semaphore(0);
    const std::string participantId("testProvisionedParticipantId");

    types::DiscoveryEntryWithMetaInfo provisionedDiscoveryEntry;
    provisionedDiscoveryEntry.setParticipantId("testProvisionedParticipantId");
    provisionedDiscoveryEntries.insert(std::make_pair(participantId, provisionedDiscoveryEntry));
    LocalDiscoveryAggregator localDiscoveryAggregator(provisionedDiscoveryEntries);
    localDiscoveryAggregator.setDiscoveryProxy(discoveryMock);

    EXPECT_CALL(*discoveryMock, lookupAsyncMock(_, _, _, _)).Times(0);

    auto onSuccess = [&semaphore, &provisionedDiscoveryEntry](
            const types::DiscoveryEntryWithMetaInfo& entry) {
        EXPECT_EQ(entry, provisionedDiscoveryEntry);
        semaphore.notify();
    };

    auto future = localDiscoveryAggregator.lookupAsync(participantId, onSuccess, nullptr);

    types::DiscoveryEntryWithMetaInfo result;
    future->get(100, result);

    EXPECT_EQ(result, provisionedDiscoveryEntry);

    EXPECT_TRUE(semaphore.waitFor(std::chrono::milliseconds(100)));
}

TEST_F(LocalDiscoveryAggregatorTest, addAsync_callsProxyWithGbids)
{
    localDiscoveryAggregator.setDiscoveryProxy(discoveryMock);

    types::DiscoveryEntryWithMetaInfo discoveryEntry;
    discoveryEntry.setParticipantId("testParticipantId");
    std::vector<std::string> gbids = { "joynrdefaultgbid", "othergbid" };
    std::vector<std::string> capturedGbids;
    Future<void> future;
    auto onSuccess = [&future]() { future.onSuccess(); };
    auto onError = [&future](const exceptions::JoynrRuntimeException& exception) {
        future.onError(std::make_shared<exceptions::JoynrRuntimeException>(exception));
    };
    auto onApplicationError = [&future](const joynr::types::DiscoveryError::Enum& errorEnum) {
        future.onError(std::make_shared<exceptions::JoynrRuntimeException>(joynr::types::DiscoveryError::getLiteral(errorEnum)));
    };
    auto mockFuture = std::make_shared<joynr::Future<void>>();
    mockFuture->onSuccess();

    EXPECT_CALL(*discoveryMock, addAsyncMock(Eq(discoveryEntry), Eq(false), _, _, _, _, _)).WillOnce(
            DoAll(::testing::SaveArg<2>(&capturedGbids), InvokeArgument<3>(), Return(mockFuture)));
    localDiscoveryAggregator.addAsync(discoveryEntry, false, gbids, onSuccess, onApplicationError, onError);
    future.get();
    EXPECT_EQ(gbids, capturedGbids);
}
<|endoftext|>


<commit_before>//-----------------------------------------------
//
//	This file is part of the Siv3D Engine.
//
//	Copyright (c) 2008-2021 Ryo Suzuki
//	Copyright (c) 2016-2021 OpenSiv3D Project
//
//	Licensed under the MIT License.
//
//-----------------------------------------------

# include <algorithm>
# include <string>
# include <vector>
# include <gio/gio.h>
# include <Siv3D/PowerStatus.hpp>
# include <Siv3D/EngineLog.hpp>

namespace s3d
{
	namespace detail
	{
		const char* NAME_UPower = "org.freedesktop.UPower";
		const char* OBJECT_UPower = "/org/freedesktop/UPower";
		const char* INTERFACE_UPower = "org.freedesktop.UPower";
		const char* INTERFACE_UPower_Device = "org.freedesktop.UPower.Device";

		bool get_property(GDBusProxy *_proxy, const gchar *_property_name, const gchar *_format, void *_dest)
		{
			GVariant *variant = g_dbus_proxy_get_cached_property(_proxy, _property_name);
			if (variant != nullptr)
			{
				g_variant_get(variant, _format, _dest);
				g_variant_unref(variant);
				return true;
			}
			else
				return false;
		}

		bool get_property_and_output_log(GDBusProxy *_proxy, const gchar *_property_name, const gchar *_format, void *_dest)
		{
			bool ret = get_property(_proxy, _property_name, _format, _dest);
			if (ret == false)
			{
				LOG_FAIL(U"❌ PowerStatus: Failed to get UPower device property ({})."_fmt(Unicode::Widen(_property_name)));
			}
			return ret;
		}

		void GetPowerStatus_Linux(PowerStatus& result)
		{
			std::vector<std::string> devicePaths;

			GDBusProxy *proxy;
			GDBusConnection *conn;
			GError *error = nullptr;
			char *s;
			guint32 u;
			guint64 x;
			gboolean b;
			gdouble d;
			GVariant *variant;
			GVariantIter *iter;

			conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, &error);
			if (conn == nullptr)
			{
				LOG_FAIL(U"❌ PowerStatus: Failed to get d-bus connection.");
				return;
			}

			proxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE, nullptr,
				NAME_UPower, OBJECT_UPower, INTERFACE_UPower, nullptr, &error);
			if (proxy == nullptr)
			{
				LOG_FAIL(U"❌ PowerStatus: Failed to get d-bus proxy.");
				return;
			}

			variant = g_dbus_proxy_get_cached_property(proxy, "DaemonVersion");
			if (variant == nullptr)
			{
				LOG_FAIL(U"❌ PowerStatus: Failed to get UPower properties.");
				return;
			}
			g_variant_get(variant, "s", &s);
			g_variant_unref(variant);
			LOG_INFO(U"ℹ  PowerStatus: UPower daemon version {}"_fmt(Unicode::Widen(s)));

			variant = g_dbus_proxy_call_sync(proxy, "EnumerateDevices", nullptr,
				G_DBUS_CALL_FLAGS_NONE, -1, nullptr, &error);
			if (variant == nullptr)
			{
				LOG_FAIL(U"❌ PowerStatus: Failed to enumerate power supply devices.");
				return;
			}
			g_variant_get(variant, "(ao)", &iter);
			while (g_variant_iter_loop(iter, "o", &s))
			{
				devicePaths.push_back(s);
			}
			g_variant_iter_free(iter);
			g_variant_unref(variant);
			g_object_unref(proxy);

			result.battery = BatteryStatus::NoBattery;

			for (const auto &dev : devicePaths)
			{
				proxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE, nullptr,
					NAME_UPower, dev.c_str(), INTERFACE_UPower_Device, nullptr, &error);

				if (!get_property_and_output_log(proxy, "Type", "u", &u))
					continue;

				if (u == 1)
				{
					//line power
					if (get_property_and_output_log(proxy, "Online", "b", &b))
					{
						result.ac = b ? ACLineStatus::Online : ACLineStatus::Offline;
					}
				}
				else if (u == 2)
				{
					//battery
					if (get_property_and_output_log(proxy, "Percentage", "d", &d))
					{
						result.batteryLifePercent = static_cast<int32>(d);
						result.battery = d <= 5 ? BatteryStatus::Critical
							: d <= 33 ? BatteryStatus::Low
							: d <= 66 ? BatteryStatus::Middle
							: BatteryStatus::High;
					}
					if (get_property_and_output_log(proxy, "State", "u", &u))
					{
						if (u == 1)
						{
							//charging
							result.charging = true;
							if (get_property_and_output_log(proxy, "TimeToFull", "x", &x))
							{
								if (x != 0)
									result.batteryTimeToFullChargeSec = static_cast<int32>(x);
							}
						}
						else if (u == 2)
						{
							//discharging
							if (get_property_and_output_log(proxy, "TimeToEmpty", "x", &x))
							{
								if (x != 0)
									result.batteryLifeTimeSec = static_cast<int32>(x);
							}
						}
					}
				}
			}

			g_object_unref(conn);
		}
	}

	namespace System
	{
		PowerStatus GetPowerStatus()
		{
			PowerStatus status;

			detail::GetPowerStatus_Linux(status);

			return status;
		}
	}
}
<commit_msg>[Linux] workaround #577<commit_after>//-----------------------------------------------
//
//	This file is part of the Siv3D Engine.
//
//	Copyright (c) 2008-2021 Ryo Suzuki
//	Copyright (c) 2016-2021 OpenSiv3D Project
//
//	Licensed under the MIT License.
//
//-----------------------------------------------

# include <algorithm>
# include <string>
# include <vector>
# include <fstream>
//# include <gio/gio.h>
# include <Siv3D/PowerStatus.hpp>
# include <Siv3D/EngineLog.hpp>

namespace s3d
{
	/*
	// [Siv3D ToDo]
	// ・時間がかかりすぎる（> 20ms）
	// ・ACLineStatus  が不正確

	namespace detail
	{
		const char* NAME_UPower = "org.freedesktop.UPower";
		const char* OBJECT_UPower = "/org/freedesktop/UPower";
		const char* INTERFACE_UPower = "org.freedesktop.UPower";
		const char* INTERFACE_UPower_Device = "org.freedesktop.UPower.Device";

		bool get_property(GDBusProxy *_proxy, const gchar *_property_name, const gchar *_format, void *_dest)
		{
			GVariant *variant = g_dbus_proxy_get_cached_property(_proxy, _property_name);
			if (variant != nullptr)
			{
				g_variant_get(variant, _format, _dest);
				g_variant_unref(variant);
				return true;
			}
			else
				return false;
		}

		bool get_property_and_output_log(GDBusProxy *_proxy, const gchar *_property_name, const gchar *_format, void *_dest)
		{
			bool ret = get_property(_proxy, _property_name, _format, _dest);
			if (ret == false)
			{
				//LOG_FAIL(U"❌ PowerStatus: Failed to get UPower device property ({})."_fmt(Unicode::Widen(_property_name)));
			}
			return ret;
		}

		void GetPowerStatus_Linux(PowerStatus& result)
		{
			std::vector<std::string> devicePaths;

			GDBusProxy *proxy;
			GDBusConnection *conn;
			GError *error = nullptr;
			char *s;
			guint32 u;
			guint64 x;
			gboolean b;
			gdouble d;
			GVariant *variant;
			GVariantIter *iter;

			conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, &error);
			if (conn == nullptr)
			{
				//LOG_FAIL(U"❌ PowerStatus: Failed to get d-bus connection.");
				return;
			}

			proxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE, nullptr,
				NAME_UPower, OBJECT_UPower, INTERFACE_UPower, nullptr, &error);
			if (proxy == nullptr)
			{
				//LOG_FAIL(U"❌ PowerStatus: Failed to get d-bus proxy.");
				return;
			}

			variant = g_dbus_proxy_get_cached_property(proxy, "DaemonVersion");
			if (variant == nullptr)
			{
				//LOG_FAIL(U"❌ PowerStatus: Failed to get UPower properties.");
				return;
			}
			g_variant_get(variant, "s", &s);
			g_variant_unref(variant);
			//LOG_INFO(U"ℹ  PowerStatus: UPower daemon version {}"_fmt(Unicode::Widen(s)));

			variant = g_dbus_proxy_call_sync(proxy, "EnumerateDevices", nullptr,
				G_DBUS_CALL_FLAGS_NONE, -1, nullptr, &error);
			if (variant == nullptr)
			{
				//LOG_FAIL(U"❌ PowerStatus: Failed to enumerate power supply devices.");
				return;
			}
			g_variant_get(variant, "(ao)", &iter);
			while (g_variant_iter_loop(iter, "o", &s))
			{
				devicePaths.push_back(s);
			}
			g_variant_iter_free(iter);
			g_variant_unref(variant);
			g_object_unref(proxy);

			result.battery = BatteryStatus::NoBattery;

			for (const auto &dev : devicePaths)
			{
				proxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE, nullptr,
					NAME_UPower, dev.c_str(), INTERFACE_UPower_Device, nullptr, &error);

				if (!get_property_and_output_log(proxy, "Type", "u", &u))
					continue;

				if (u == 1)
				{
					//line power
					if (get_property_and_output_log(proxy, "Online", "b", &b))
					{
						result.ac = b ? ACLineStatus::Online : ACLineStatus::Offline;
					}
				}
				else if (u == 2)
				{
					//battery
					if (get_property_and_output_log(proxy, "Percentage", "d", &d))
					{
						result.batteryLifePercent = static_cast<int32>(d);
						result.battery = d <= 5 ? BatteryStatus::Critical
							: d <= 33 ? BatteryStatus::Low
							: d <= 66 ? BatteryStatus::Middle
							: BatteryStatus::High;
					}
					if (get_property_and_output_log(proxy, "State", "u", &u))
					{
						if (u == 1)
						{
							//charging
							result.charging = true;
							if (get_property_and_output_log(proxy, "TimeToFull", "x", &x))
							{
								if (x != 0)
									result.batteryTimeToFullChargeSec = static_cast<int32>(x);
							}
						}
						else if (u == 2)
						{
							//discharging
							if (get_property_and_output_log(proxy, "TimeToEmpty", "x", &x))
							{
								if (x != 0)
									result.batteryLifeTimeSec = static_cast<int32>(x);
							}
						}
					}
				}
			}

			g_object_unref(conn);
		}
	}

	namespace System
	{
		PowerStatus GetPowerStatus()
		{
			PowerStatus status;

			detail::GetPowerStatus_Linux(status);

			return status;
		}
	}
	*/

	namespace detail
	{
		[[nodiscard]]
		static Optional<int32> ReadInt(const char* path)
		{
			std::ifstream ifs{ path };

			if (not ifs)
			{
				return none;
			}

			int32 value;

			if (ifs >> value)
			{
				return value;
			}
			else
			{
				return none;
			}
		}

		[[nodiscard]]
		static Optional<std::string> ReadString(const char* path)
		{
			std::ifstream ifs{ path };

			if (not ifs)
			{
				return none;
			}

			std::string value;

			if (ifs >> value)
			{
				return value;
			}
			else
			{
				return none;
			}
		}
	}

	namespace System
	{
		PowerStatus GetPowerStatus()
		{
			PowerStatus status;
			status.battery = BatteryStatus::NoBattery;
			bool hasBattery = false;

			if (auto value = detail::ReadInt("/sys/class/power_supply/AC/online"))
			{
				status.ac = (*value == 1) ? ACLineStatus::Online : ACLineStatus::Offline;
			}
			else if (auto value = detail::ReadInt("/sys/class/power_supply/ac/online"))
			{
				status.ac = (*value == 1) ? ACLineStatus::Online : ACLineStatus::Offline;
			}

			if (auto value = detail::ReadString("/sys/class/power_supply/battery/status"))
			{
				hasBattery = true;
				status.charging = (*value == "Charging");

				if (auto capacity = detail::ReadInt("/sys/class/power_supply/battery/capacity"))
				{
					status.batteryLifePercent = capacity;
				}
			}
			else if (auto value = detail::ReadString("/sys/class/power_supply/BAT0/status"))
			{
				hasBattery = true;
				status.charging = (*value == "Charging");

				if (auto capacity = detail::ReadInt("/sys/class/power_supply/BAT0/capacity"))
				{
					status.batteryLifePercent = capacity;
				}
			}

			if (hasBattery)
			{
				if (status.batteryLifePercent)
				{
					const int32 percent = *status.batteryLifePercent;

					status.battery = percent <+ 5 ? BatteryStatus::Critical
					: percent <= 33 ? BatteryStatus::Low
					: percent <= 66 ? BatteryStatus::Middle
					: BatteryStatus::High;
				}
				else
				{
					status.battery = BatteryStatus::Unknown;
				}
			}

			return status;
		}
	}
}
<|endoftext|>


<commit_before>/** \brief Utility for generating ISSN mapping tables from Zeder.
 *  \author Madeeswaran Kannan (madeeswaran.kannan@uni-tuebingen.de)
 *
 *  \copyright 2019 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <functional>
#include <iostream>
#include <unordered_set>
#include <cstdio>
#include <cstdlib>
#include "BSZTransform.h"
#include "ExecUtil.h"
#include "File.h"
#include "FileUtil.h"
#include "MiscUtil.h"
#include "StringUtil.h"
#include "util.h"
#include "Zeder.h"

namespace {

[[noreturn]] void Usage() {
    ::Usage("[--find-duplicate-issns] [--push-to-github] issn_map_directory <map-type=filename> <map-type=filename>...\n\n"
            "Valid map type(s): ssg");
}

enum MapType { SSG };

struct MapParams {
    std::string type_string_;
    std::string zeder_column_;
    std::function<std::string(const std::string &, const unsigned)> convert_zeder_value_to_map_value_;
};

std::string ZederToMapValueSSGN(const std::string &zeder_value, const unsigned zeder_id) {
    switch (BSZTransform::GetSSGNTypeFromString(zeder_value)) {
    case BSZTransform::SSGNType::FG_0:
        return "0";
    case BSZTransform::SSGNType::FG_1:
        return "1";
    case BSZTransform::SSGNType::FG_01:
        return "0/1";
    case BSZTransform::SSGNType::INVALID:
        LOG_ERROR("invalid Zeder value for SSGN '" + zeder_value + "' (Zeder ID: " + std::to_string(zeder_id) + ")");
    }
}

const std::map<MapType, MapParams> MAP_TYPE_TO_PARAMS {
    { MapType::SSG, { "ssg", "ber", ZederToMapValueSSGN } }
};

const std::map<std::string, MapType> STRING_TO_MAP_TYPE {
    { "ssg", MapType::SSG }
};

void ParseMapPairs(int argc, char * argv[], std::map<MapType, std::string> * const map_type_to_filename) {
    std::vector<std::string> splits;

    for (int i(1); i < argc; ++i) {
        StringUtil::Split(std::string(argv[i]), std::string("="), &splits);
        if (splits.size() != 2)
            Usage();

        const auto match(STRING_TO_MAP_TYPE.find(splits[0]));
        if (match == STRING_TO_MAP_TYPE.end())
            Usage();
        map_type_to_filename->insert(std::make_pair(match->second, splits[1]));
    }
}

void DownloadFullDumpFromZeder(Zeder::Flavour flavour, Zeder::EntryCollection * const entries) {
    std::unordered_set<std::string> columns_to_download;
    std::unordered_set<unsigned> entries_to_download;
    std::unordered_map<std::string, std::string> filter_regexps;

    std::unique_ptr<Zeder::FullDumpDownloader::Params> params(
        new Zeder::FullDumpDownloader::Params(Zeder::GetFullDumpEndpointPath(flavour),
                                              entries_to_download, columns_to_download, filter_regexps));

    Zeder::EndpointDownloader::Factory(Zeder::EndpointDownloader::FULL_DUMP, std::move(params))->download(entries);
}

struct MapValue {
    std::string issn_;              // print or online
    std::string journal_title_;
    unsigned zeder_id_;
    std::string value_;

    MapValue(const std::string issn, const std::string journal_title, const unsigned zeder_id, const std::string &value)
     : issn_(issn), journal_title_(journal_title), zeder_id_(zeder_id), value_(value) {}
};

void GenerateISSNMap(Zeder::EntryCollection &entries, const MapParams &params,
                     std::vector<MapValue> * const map_values)
{
    static const std::string ZEDER_TITLE_COLUMN("tit");
    static const std::string ZEDER_ISSN_COLUMN("issn");
    static const std::string ZEDER_ESSN_COLUMN("essn");

    map_values->clear();

    for (const auto &entry : entries) {
        const auto zeder_value(entry.getAttribute(params.zeder_column_, ""));
        if (zeder_value.empty()) {
            LOG_DEBUG("Skipping zeder entry " + std::to_string(entry.getId()) + " with no value for '" + params.zeder_column_ + "'");
            continue;
        }

        std::set<std::string> issns, scratch;
        StringUtil::Split(entry.getAttribute(ZEDER_ISSN_COLUMN, ""), std::set<char>{ ' ', ','}, &scratch,
                          /* suppress_empty_tokens = */ true);
        for (const auto &split : scratch) {
            if (MiscUtil::IsPossibleISSN(split))
                issns.insert(split);
        }

        StringUtil::Split(entry.getAttribute(ZEDER_ESSN_COLUMN, ""), std::set<char>{ ' ', ','}, &scratch,
                          /* suppress_empty_tokens = */ true);
        for (const auto &split : scratch) {
            if (MiscUtil::IsPossibleISSN(split))
                issns.insert(split);
        }

        for (const auto &issn : issns) {
            const auto converted_value(params.convert_zeder_value_to_map_value_(zeder_value, entry.getId()));
            map_values->emplace_back(issn, entry.getAttribute(ZEDER_TITLE_COLUMN),
                                     entry.getId(), converted_value);
        }
    }
}

void FindDuplicateISSNs(const std::vector<MapValue> &map_values) {
    std::unordered_set<std::string> processed_issns;
    std::unordered_multimap<std::string, unsigned> processed_values;

    for (const auto &value : map_values) {
        processed_values.insert(std::make_pair(value.issn_, value.zeder_id_));
        processed_issns.insert(value.issn_);
    }

    for (const auto &issn : processed_issns) {
        if (processed_values.count(issn) > 1) {
            const auto range(processed_values.equal_range(issn));

            std::string warning_message("ISSN '" + issn + "' found in multiple Zeder entries: ");
            for (auto itr(range.first); itr != range.second; ++itr)
                warning_message += std::to_string(itr->second) + " ";

            LOG_WARNING(warning_message);
        }
    }
}

void WriteMapValuesToFile(const std::vector<MapValue> &map_values, const MapParams &map_params, const std::string &file_path) {
    File output(file_path, "w");

    for (const auto &value : map_values) {
        output.writeln(value.issn_ + "=" + value.value_ + " # (" + std::to_string(value.zeder_id_) + ") "
                       + value.journal_title_);
    }

    LOG_INFO("Wrote " + std::to_string(map_values.size()) + " entries to " + map_params.type_string_ + " map '"
                 + file_path + "'");
}

/*
void PushToGitHub(const std::string &issn_directory, const std::vector<std::string> &files_to_push) {

}
*/

} // unnamed namespace

int Main(int argc, char *argv[]) {
    if (argc < 3)
        Usage();

    bool find_duplicate_issns(false);
    const std::string FIND_DUPLICATE_ISSNS_FLAG("--find-duplicate-issns");
    if (StringUtil::StartsWith(argv[1], FIND_DUPLICATE_ISSNS_FLAG)) {
        find_duplicate_issns = true;
        --argc, ++argv;
    }

    bool push_to_github(false);
    const std::string COMMIT_AND_PUSH_FLAG("--push-to-github");
    if (StringUtil::StartsWith(argv[1], COMMIT_AND_PUSH_FLAG)) {
        push_to_github = true;
        --argc, ++argv;
    }

    if (argc < 3)
        Usage();

    const std::string issn_directory(argv[1]);
    --argc, ++argv;

    std::map<MapType, std::string> map_filename_pairs;
    ParseMapPairs(argc, argv, &map_filename_pairs);

    Zeder::EntryCollection entries;
    DownloadFullDumpFromZeder(Zeder::Flavour::IXTHEO, &entries);

    std::vector<MapValue> map_values;
    std::vector<std::string> files_to_push;

    for (const auto &map_filename_pair : map_filename_pairs) {
        const auto &map_params(MAP_TYPE_TO_PARAMS.at(map_filename_pair.first));
        const std::string output_file(issn_directory + "/" + map_filename_pair.second);

        GenerateISSNMap(entries, map_params, &map_values);
        if (find_duplicate_issns)
            FindDuplicateISSNs(map_values);

        WriteMapValuesToFile(map_values, map_params, output_file);

        if (push_to_github)
            files_to_push.emplace_back(output_file);
    }

//    if (push_to_github)
//        PushToGitHub(issn_directory, files_to_push);

    return EXIT_SUCCESS;
}
<commit_msg>Code style<commit_after>/** \brief Utility for generating ISSN mapping tables from Zeder.
 *  \author Madeeswaran Kannan (madeeswaran.kannan@uni-tuebingen.de)
 *
 *  \copyright 2019 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <functional>
#include <iostream>
#include <unordered_set>
#include <cstdio>
#include <cstdlib>
#include "BSZTransform.h"
#include "ExecUtil.h"
#include "File.h"
#include "FileUtil.h"
#include "MiscUtil.h"
#include "StringUtil.h"
#include "util.h"
#include "Zeder.h"

namespace {

const std::string FIND_DUPLICATE_ISSNS_FLAG("--find-duplicate-issns");
const std::string COMMIT_AND_PUSH_FLAG("--push-to-github");

[[noreturn]] void Usage() {
    ::Usage("[" + FIND_DUPLICATE_ISSNS_FLAG + "] [" + COMMIT_AND_PUSH_FLAG + "] issn_map_directory <map-type=filename>"
            "<map-type=filename>...\n\nValid map type(s): ssg");
}

enum MapType { SSG };

struct MapParams {
    std::string type_string_;
    std::string zeder_column_;
    std::function<std::string(const std::string &, const unsigned)> convert_zeder_value_to_map_value_;
};

std::string ZederToMapValueSSGN(const std::string &zeder_value, const unsigned zeder_id) {
    switch (BSZTransform::GetSSGNTypeFromString(zeder_value)) {
    case BSZTransform::SSGNType::FG_0:
        return "0";
    case BSZTransform::SSGNType::FG_1:
        return "1";
    case BSZTransform::SSGNType::FG_01:
        return "0/1";
    case BSZTransform::SSGNType::INVALID:
        LOG_ERROR("invalid Zeder value for SSGN '" + zeder_value + "' (Zeder ID: " + std::to_string(zeder_id) + ")");
    }
}

const std::map<MapType, MapParams> MAP_TYPE_TO_PARAMS {
    { MapType::SSG, { "ssg", "ber", ZederToMapValueSSGN } }
};

const std::map<std::string, MapType> STRING_TO_MAP_TYPE {
    { "ssg", MapType::SSG }
};

void ParseMapPairs(int argc, char * argv[], std::map<MapType, std::string> * const map_type_to_filename) {
    std::vector<std::string> splits;

    for (int i(1); i < argc; ++i) {
        StringUtil::Split(std::string(argv[i]), std::string("="), &splits);
        if (splits.size() != 2)
            Usage();

        const auto match(STRING_TO_MAP_TYPE.find(splits[0]));
        if (match == STRING_TO_MAP_TYPE.end())
            Usage();
        map_type_to_filename->insert(std::make_pair(match->second, splits[1]));
    }
}

void DownloadFullDumpFromZeder(Zeder::Flavour flavour, Zeder::EntryCollection * const entries) {
    std::unordered_set<std::string> columns_to_download;
    std::unordered_set<unsigned> entries_to_download;
    std::unordered_map<std::string, std::string> filter_regexps;

    std::unique_ptr<Zeder::FullDumpDownloader::Params> params(
        new Zeder::FullDumpDownloader::Params(Zeder::GetFullDumpEndpointPath(flavour),
                                              entries_to_download, columns_to_download, filter_regexps));

    Zeder::EndpointDownloader::Factory(Zeder::EndpointDownloader::FULL_DUMP, std::move(params))->download(entries);
}

struct MapValue {
    std::string issn_;              // print or online
    std::string journal_title_;
    unsigned zeder_id_;
    std::string value_;

    MapValue(const std::string issn, const std::string journal_title, const unsigned zeder_id, const std::string &value)
     : issn_(issn), journal_title_(journal_title), zeder_id_(zeder_id), value_(value) {}
};

void GenerateISSNMap(Zeder::EntryCollection &entries, const MapParams &params,
                     std::vector<MapValue> * const map_values)
{
    static const std::string ZEDER_TITLE_COLUMN("tit");
    static const std::string ZEDER_ISSN_COLUMN("issn");
    static const std::string ZEDER_ESSN_COLUMN("essn");

    map_values->clear();

    for (const auto &entry : entries) {
        const auto zeder_value(entry.getAttribute(params.zeder_column_, ""));
        if (zeder_value.empty()) {
            LOG_DEBUG("Skipping zeder entry " + std::to_string(entry.getId()) + " with no value for '" + params.zeder_column_ + "'");
            continue;
        }

        std::set<std::string> issns, scratch;
        StringUtil::Split(entry.getAttribute(ZEDER_ISSN_COLUMN, ""), std::set<char>{ ' ', ','}, &scratch,
                          /* suppress_empty_tokens = */ true);
        for (const auto &split : scratch) {
            if (MiscUtil::IsPossibleISSN(split))
                issns.insert(split);
        }

        StringUtil::Split(entry.getAttribute(ZEDER_ESSN_COLUMN, ""), std::set<char>{ ' ', ','}, &scratch,
                          /* suppress_empty_tokens = */ true);
        for (const auto &split : scratch) {
            if (MiscUtil::IsPossibleISSN(split))
                issns.insert(split);
        }

        for (const auto &issn : issns) {
            const auto converted_value(params.convert_zeder_value_to_map_value_(zeder_value, entry.getId()));
            map_values->emplace_back(issn, entry.getAttribute(ZEDER_TITLE_COLUMN),
                                     entry.getId(), converted_value);
        }
    }
}

void FindDuplicateISSNs(const std::vector<MapValue> &map_values) {
    std::unordered_set<std::string> processed_issns;
    std::unordered_multimap<std::string, unsigned> processed_values;

    for (const auto &value : map_values) {
        processed_values.insert(std::make_pair(value.issn_, value.zeder_id_));
        processed_issns.insert(value.issn_);
    }

    for (const auto &issn : processed_issns) {
        if (processed_values.count(issn) > 1) {
            const auto range(processed_values.equal_range(issn));

            std::string warning_message("ISSN '" + issn + "' found in multiple Zeder entries: ");
            for (auto itr(range.first); itr != range.second; ++itr)
                warning_message += std::to_string(itr->second) + " ";

            LOG_WARNING(warning_message);
        }
    }
}

void WriteMapValuesToFile(const std::vector<MapValue> &map_values, const MapParams &map_params, const std::string &file_path) {
    File output(file_path, "w");

    for (const auto &value : map_values) {
        output.writeln(value.issn_ + "=" + value.value_ + " # (" + std::to_string(value.zeder_id_) + ") "
                       + value.journal_title_);
    }

    LOG_INFO("Wrote " + std::to_string(map_values.size()) + " entries to " + map_params.type_string_ + " map '"
                 + file_path + "'");
}

/*
void PushToGitHub(const std::string &issn_directory, const std::vector<std::string> &files_to_push) {

}
*/

} // unnamed namespace

int Main(int argc, char *argv[]) {
    if (argc < 3)
        Usage();

    bool find_duplicate_issns(false);
    if (StringUtil::StartsWith(argv[1], FIND_DUPLICATE_ISSNS_FLAG)) {
        find_duplicate_issns = true;
        --argc, ++argv;
    }

    bool push_to_github(false);
    if (StringUtil::StartsWith(argv[1], COMMIT_AND_PUSH_FLAG)) {
        push_to_github = true;
        --argc, ++argv;
    }

    if (argc < 3)
        Usage();

    const std::string issn_directory(argv[1]);
    --argc, ++argv;

    std::map<MapType, std::string> map_filename_pairs;
    ParseMapPairs(argc, argv, &map_filename_pairs);

    Zeder::EntryCollection entries;
    DownloadFullDumpFromZeder(Zeder::Flavour::IXTHEO, &entries);

    std::vector<MapValue> map_values;
    std::vector<std::string> files_to_push;

    for (const auto &map_filename_pair : map_filename_pairs) {
        const auto &map_params(MAP_TYPE_TO_PARAMS.at(map_filename_pair.first));
        const std::string output_file(issn_directory + "/" + map_filename_pair.second);

        GenerateISSNMap(entries, map_params, &map_values);
        if (find_duplicate_issns)
            FindDuplicateISSNs(map_values);

        WriteMapValuesToFile(map_values, map_params, output_file);

        if (push_to_github)
            files_to_push.emplace_back(output_file);
    }

//    if (push_to_github)
//        PushToGitHub(issn_directory, files_to_push);

    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>#include <mapnik/map.hpp>
#include <mapnik/load_map.hpp>
#include <mapnik/agg_renderer.hpp>
#include <mapnik/version.hpp>
#include <mapnik/debug.hpp>
#include <mapnik/image_util.hpp>
#include <mapnik/unicode.hpp>
#include <mapnik/datasource_cache.hpp>
#include <mapnik/font_engine_freetype.hpp>

#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/program_options.hpp>
#pragma GCC diagnostic pop

#include <string>

int main (int argc,char** argv)
{
    namespace po = boost::program_options;

    bool verbose = false;
    bool auto_open = true;
    int return_value = 0;
    std::string xml_file;
    std::string img_file;
    double scale_factor = 1;
    bool params_as_variables = false;
    mapnik::logger logger;
    logger.set_severity(mapnik::logger::error);

    try
    {
        po::options_description desc("nik2img utility");
        desc.add_options()
            ("help,h", "produce usage message")
            ("version,V","print version string")
            ("verbose,v","verbose output")
            ("open","automatically open the file after rendering (os x only)")
            ("xml",po::value<std::string>(),"xml map to read")
            ("img",po::value<std::string>(),"image to render")
            ("scale-factor",po::value<double>(),"scale factor for rendering")
            ("variables","make map parameters available as render-time variables")
            ;

        po::positional_options_description p;
        p.add("xml",1);
        p.add("img",1);
        po::variables_map vm;
        po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), vm);
        po::notify(vm);

        if (vm.count("version"))
        {
            std::clog <<"version " << MAPNIK_VERSION_STRING << std::endl;
            return 1;
        }

        if (vm.count("help"))
        {
            std::clog << desc << std::endl;
            return 1;
        }

        if (vm.count("verbose"))
        {
            verbose = true;
        }

        if (vm.count("open"))
        {
            auto_open = true;
        }

        if (vm.count("xml"))
        {
            xml_file=vm["xml"].as<std::string>();
        }
        else
        {
            std::clog << "please provide an xml map as first argument!" << std::endl;
            return -1;
        }

        if (vm.count("img"))
        {
            img_file=vm["img"].as<std::string>();
        }
        else
        {
            std::clog << "please provide an img as second argument!" << std::endl;
            return -1;
        }

        if (vm.count("scale-factor"))
        {
            scale_factor=vm["scale-factor"].as<double>();
        }

        if (vm.count("variables"))
        {
            params_as_variables = true;
        }

        mapnik::datasource_cache::instance().register_datasources("./plugins/input/");
        mapnik::freetype_engine::register_fonts("./fonts",true);
        mapnik::Map map(600,400);
        mapnik::load_map(map,xml_file,true);
        map.zoom_all();
        mapnik::image_rgba8 im(map.width(),map.height());
        mapnik::request req(map.width(),map.height(),map.get_current_extent());
        req.set_buffer_size(map.buffer_size());
        mapnik::attributes vars;
        if (params_as_variables)
        {
            mapnik::transcoder tr("utf-8");
            for (auto const& param : map.get_extra_parameters())
            {
                std::string const& name = param.first.substr(1);
                if (!name.empty())
                {
                    if (param.second.is<mapnik::value_integer>())
                    {
                        vars[name] = param.second.get<mapnik::value_integer>();
                    }
                    else if (param.second.is<mapnik::value_double>())
                    {
                        vars[name] = param.second.get<mapnik::value_double>();
                    }
                    else if (param.second.is<std::string>())
                    {
                        vars[name] = tr.transcode(param.second.get<std::string>().c_str());
                    }
                }
            }
        }
        mapnik::agg_renderer<mapnik::image_rgba8> ren(map,req,vars,im,scale_factor,0,0);
        ren.apply();
        mapnik::save_to_file(im,img_file);
        if (auto_open)
        {
            std::ostringstream s;
#ifdef __APPLE__
            s << "open ";
#elif _WIN32
            s << "start ";
#else
            s << "xdg-open ";
#endif
            s << img_file;
            int ret = system(s.str().c_str());
            if (ret != 0)
                return_value = ret;
        }
        else
        {
            std::clog << "rendered to: " << img_file << "\n";
        }
    }
    catch (std::exception const& ex)
    {
        std::clog << "Error " << ex.what() << std::endl;
        return -1;
    }
    return return_value;
}
<commit_msg>Fix name reported by mapnik-render<commit_after>#include <mapnik/map.hpp>
#include <mapnik/load_map.hpp>
#include <mapnik/agg_renderer.hpp>
#include <mapnik/version.hpp>
#include <mapnik/debug.hpp>
#include <mapnik/image_util.hpp>
#include <mapnik/unicode.hpp>
#include <mapnik/datasource_cache.hpp>
#include <mapnik/font_engine_freetype.hpp>

#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/program_options.hpp>
#pragma GCC diagnostic pop

#include <string>

int main (int argc,char** argv)
{
    namespace po = boost::program_options;

    bool verbose = false;
    bool auto_open = true;
    int return_value = 0;
    std::string xml_file;
    std::string img_file;
    double scale_factor = 1;
    bool params_as_variables = false;
    mapnik::logger logger;
    logger.set_severity(mapnik::logger::error);

    try
    {
        po::options_description desc("mapnik-render utility");
        desc.add_options()
            ("help,h", "produce usage message")
            ("version,V","print version string")
            ("verbose,v","verbose output")
            ("open","automatically open the file after rendering (os x only)")
            ("xml",po::value<std::string>(),"xml map to read")
            ("img",po::value<std::string>(),"image to render")
            ("scale-factor",po::value<double>(),"scale factor for rendering")
            ("variables","make map parameters available as render-time variables")
            ;

        po::positional_options_description p;
        p.add("xml",1);
        p.add("img",1);
        po::variables_map vm;
        po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), vm);
        po::notify(vm);

        if (vm.count("version"))
        {
            std::clog <<"version " << MAPNIK_VERSION_STRING << std::endl;
            return 1;
        }

        if (vm.count("help"))
        {
            std::clog << desc << std::endl;
            return 1;
        }

        if (vm.count("verbose"))
        {
            verbose = true;
        }

        if (vm.count("open"))
        {
            auto_open = true;
        }

        if (vm.count("xml"))
        {
            xml_file=vm["xml"].as<std::string>();
        }
        else
        {
            std::clog << "please provide an xml map as first argument!" << std::endl;
            return -1;
        }

        if (vm.count("img"))
        {
            img_file=vm["img"].as<std::string>();
        }
        else
        {
            std::clog << "please provide an img as second argument!" << std::endl;
            return -1;
        }

        if (vm.count("scale-factor"))
        {
            scale_factor=vm["scale-factor"].as<double>();
        }

        if (vm.count("variables"))
        {
            params_as_variables = true;
        }

        mapnik::datasource_cache::instance().register_datasources("./plugins/input/");
        mapnik::freetype_engine::register_fonts("./fonts",true);
        mapnik::Map map(600,400);
        mapnik::load_map(map,xml_file,true);
        map.zoom_all();
        mapnik::image_rgba8 im(map.width(),map.height());
        mapnik::request req(map.width(),map.height(),map.get_current_extent());
        req.set_buffer_size(map.buffer_size());
        mapnik::attributes vars;
        if (params_as_variables)
        {
            mapnik::transcoder tr("utf-8");
            for (auto const& param : map.get_extra_parameters())
            {
                std::string const& name = param.first.substr(1);
                if (!name.empty())
                {
                    if (param.second.is<mapnik::value_integer>())
                    {
                        vars[name] = param.second.get<mapnik::value_integer>();
                    }
                    else if (param.second.is<mapnik::value_double>())
                    {
                        vars[name] = param.second.get<mapnik::value_double>();
                    }
                    else if (param.second.is<std::string>())
                    {
                        vars[name] = tr.transcode(param.second.get<std::string>().c_str());
                    }
                }
            }
        }
        mapnik::agg_renderer<mapnik::image_rgba8> ren(map,req,vars,im,scale_factor,0,0);
        ren.apply();
        mapnik::save_to_file(im,img_file);
        if (auto_open)
        {
            std::ostringstream s;
#ifdef __APPLE__
            s << "open ";
#elif _WIN32
            s << "start ";
#else
            s << "xdg-open ";
#endif
            s << img_file;
            int ret = system(s.str().c_str());
            if (ret != 0)
                return_value = ret;
        }
        else
        {
            std::clog << "rendered to: " << img_file << "\n";
        }
    }
    catch (std::exception const& ex)
    {
        std::clog << "Error " << ex.what() << std::endl;
        return -1;
    }
    return return_value;
}
<|endoftext|>


<commit_before>/* Copyright 2016 Stanford University, NVIDIA Corporation
 *
 * 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.
 */

// This example demonstrates way in which virtual mapping can be useful -
// generating variable-sized output from a task

#include <legion.h>
#include <realm/cmdline.h>
#include <mappers/default_mapper.h>

using namespace Legion;
using namespace Legion::Mapping;
using namespace LegionRuntime::Accessor;

enum TaskID
{
  TOP_LEVEL_TASK_ID,
  MAKE_DATA_TASK_ID,
  USE_DATA_TASK_ID,
};

enum {
  FID_DATA = 55,
};

enum {
  PID_ALLOCED_DATA = 4,
};

enum {
  PFID_USE_DATA_TASK = 77,
};

LegionRuntime::Logger::Category log_app("app");

struct MakeDataTaskArgs {
  int random_seed;
  size_t average_output_size;
};

void top_level_task(const Task *task,
                    const std::vector<PhysicalRegion> &regions,
                    Context ctx, Runtime *runtime)
{
  size_t num_subregions = 4;
  size_t average_output_size = 100;
  size_t total_region_size = 1 << 30;
  int random_seed = 12345;
  bool use_projection_functor = false;

  const InputArgs &command_args = HighLevelRuntime::get_input_args();

  bool ok = Realm::CommandLineParser()
    .add_option_int("-n", num_subregions)
    .add_option_int("-s", average_output_size)
    .add_option_int("-l", total_region_size)
    .add_option_int("-seed", random_seed)
    .add_option_bool("-proj", use_projection_functor)
    .parse_command_line(command_args.argc, (const char **)command_args.argv);

  if(!ok) {
    log_app.fatal() << "error parsing command line arguments";
    exit(1);
  }

  // create a top-level region that is (easily) large enough to hold all of our
  // data - we need not worry about a tight bound, because we will never create
  // any instances of this size
  Rect<1> bounds(0, total_region_size - 1);
  IndexSpace is = runtime->create_index_space(ctx, Domain::from_rect<1>(bounds));
  runtime->attach_name(is, "is");

  // our field space will just have a single field
  FieldSpace fs = runtime->create_field_space(ctx);
  runtime->attach_name(fs, "fs");
  {
    FieldAllocator fsa = runtime->create_field_allocator(ctx, fs);
    fsa.allocate_field(sizeof(int), FID_DATA);
    runtime->attach_name(fs, FID_DATA, "data");
  }

  LogicalRegion lr = runtime->create_logical_region(ctx, is, fs);
  runtime->attach_name(lr, "lr");

  // partition it into equal pieces - again, these are expected to be much
  // larger than the actual output of the generator tasks
  Rect<1> subregion_idxs(0, num_subregions - 1);
  size_t elements_per_subregion = total_region_size / num_subregions;
  assert(elements_per_subregion >= (2 * average_output_size));
  Blockify<1> blockify(elements_per_subregion);
  IndexPartition ip = runtime->create_index_partition(ctx, is, blockify);
  LogicalPartition lp = runtime->get_logical_partition(ctx, lr, ip);

  // perform an index launch to have each subregion filled in with a variable
  // amount of data
  {
    MakeDataTaskArgs args;
    args.random_seed = random_seed;
    args.average_output_size = average_output_size;
    IndexLauncher launcher(MAKE_DATA_TASK_ID, Domain::from_rect<1>(subregion_idxs),
			   TaskArgument(&args, sizeof(args)),
			   ArgumentMap());
    launcher.add_region_requirement(RegionRequirement(lp,
						      0, // identity projection
						      READ_WRITE,
						      EXCLUSIVE,
						      lr,
						      DefaultMapper::VIRTUAL_MAP)
				    .add_field(FID_DATA));
    FutureMap fm = runtime->execute_index_space(ctx, launcher);
    fm.wait_all_results();
  }

  // now perform a second index launch to use the variable-sized chunks of
  // data
  {
    IndexLauncher launcher(USE_DATA_TASK_ID, Domain::from_rect<1>(subregion_idxs),
			   TaskArgument(0, 0),
			   ArgumentMap());
    if(use_projection_functor) {
      // use a projection functor to tell the runtime precisely which sub-subregion
      // each point task will use so that it can be mapped before the task executes -
      // make sure the default mapper doesn't try to map the parent region though
      launcher.add_region_requirement(RegionRequirement(lp,
							PFID_USE_DATA_TASK,
							READ_ONLY,
							EXCLUSIVE,
							lr,
							DefaultMapper::EXACT_REGION)
				      .add_field(FID_DATA));
    } else {
      // OR, use another virtual mapping and let the use_data_task walk the region
      // tree itself and do an inline mapping within the task
      launcher.add_region_requirement(RegionRequirement(lp,
							0, // identity projection
							READ_ONLY,
							EXCLUSIVE,
							lr,
							DefaultMapper::VIRTUAL_MAP)
				      .add_field(FID_DATA));
    }
    FutureMap fm = runtime->execute_index_space(ctx, launcher);
    fm.wait_all_results();
  }
}

void make_data_task(const Task *task,
                    const std::vector<PhysicalRegion> &regions,
                    Context ctx, Runtime *runtime)
{
  const MakeDataTaskArgs& args = *static_cast<const MakeDataTaskArgs *>(task->args);
  int subregion_index = task->index_point.get_point<1>();

  // we should _NOT_ have a mapping of our region
  assert(!regions[0].is_mapped());

  // first, decide how much data we're going to produce
  unsigned short xsubi[3];
  xsubi[0] = args.random_seed >> 16;
  xsubi[1] = args.random_seed;
  xsubi[2] = subregion_index;
  coord_t output_size = nrand48(xsubi) % (2 * args.average_output_size + 1);

  log_app.print() << "make data: subregion " << subregion_index << ", output size = " << output_size;

  // now we can define a (sub-)subregion of our subregion that is the right size
  // to hold the output
  LogicalRegion my_lr = regions[0].get_logical_region();
  IndexSpace my_is = my_lr.get_index_space();
  Rect<1> my_bounds = runtime->get_index_space_domain(my_is).get_rect<1>();
  coord_t bounds_lo = my_bounds.lo[0];

  // remember rectangle bounds are inclusive on both sides
  Rect<1> alloc_bounds(my_bounds.lo, bounds_lo + output_size - 1);

  // create a new partition with a known part_color so that other tasks can find
  // the color space will consist of the single color '0'
  DomainColoring dc;
  Domain color_space = Domain::from_rect<1>(Rect<1>(0, 0));
  dc[0] = Domain::from_rect<1>(alloc_bounds);

  IndexPartition alloc_ip = runtime->create_index_partition(ctx,
							    my_is,
							    color_space,
							    dc,
							    DISJOINT_KIND, // trivial
							    PID_ALLOCED_DATA);

  // now we get the name of the logical subregion we just created and inline map
  // it to generate our output
  LogicalPartition alloc_lp = runtime->get_logical_partition(my_lr, alloc_ip);
  IndexSpace alloc_is = runtime->get_index_subspace(alloc_ip, DomainPoint::from_point<1>(0));
  LogicalRegion alloc_lr = runtime->get_logical_subregion(alloc_lp, alloc_is);

  log_app.debug() << "created subregion " << alloc_lr;

  // tell the default mapper that we want exactly this region to be mapped
  // otherwise, its heuristics may cause it to try to map the (huge) parent
  InlineLauncher launcher(RegionRequirement(alloc_lr,
					    READ_WRITE,
					    EXCLUSIVE,
					    my_lr,
					    DefaultMapper::EXACT_REGION)
			  .add_field(FID_DATA));
  PhysicalRegion pr = runtime->map_region(ctx, launcher);

  // this would be done as part of asking for an accessor, but do it explicitly for
  // didactic purposes - while inline mappings can often be expensive due to stalls or
  // data movement, this should be fast because we are just allocating new space
  pr.wait_until_valid();

  {
    RegionAccessor<AccessorType::Generic, int> ra = pr.get_field_accessor(FID_DATA).typeify<int>();

    for(coord_t i = 0; i < output_size; i++)
      ra.write(DomainPoint::from_point<1>(bounds_lo + i),
	       (subregion_index * 10000) + i);
  }

  // release our inline mapping
  runtime->unmap_region(ctx, pr);

  // no need to explicitly return the name of the subregion we created - it can be foud
  // by walking the region tree using known colors
}

void use_data_task(const Task *task,
		   const std::vector<PhysicalRegion> &regions,
		   Context ctx, Runtime *runtime)
{
  int subregion_index = task->index_point.get_point<1>();

  PhysicalRegion pr = regions[0];
  Rect<1> my_bounds;
  bool was_virtual_mapped = !regions[0].is_mapped();
  if(was_virtual_mapped) {
    // find the actual subregion containing our input data by walking the
    // region tree
    LogicalRegion my_lr = regions[0].get_logical_region();
    IndexSpace my_is = my_lr.get_index_space();

    IndexPartition alloc_ip = runtime->get_index_partition(my_is, PID_ALLOCED_DATA);

    // getting from the index partition to the logical subregion is identical to
    // what was done in make_data_task()
    LogicalPartition alloc_lp = runtime->get_logical_partition(my_lr, alloc_ip);
    IndexSpace alloc_is = runtime->get_index_subspace(alloc_ip, DomainPoint::from_point<1>(0));
    LogicalRegion alloc_lr = runtime->get_logical_subregion(alloc_lp, alloc_is);

    log_app.debug() << "looked up subregion " << alloc_lr;

    // learn the input size from the bounds of the allocated subspace
    my_bounds = runtime->get_index_space_domain(alloc_is).get_rect<1>();

    // again, tell the default mapper that we want exactly this region to be mapped
    // this is important if the mapper sends us to a different place than where the
    // data was produced and a copy is needed
    InlineLauncher launcher(RegionRequirement(alloc_lr,
					      READ_ONLY,
					      EXCLUSIVE,
					      my_lr,
					      DefaultMapper::EXACT_REGION)
			    .add_field(FID_DATA));
    pr = runtime->map_region(ctx, launcher);
  } else {
    // we've got the exact region mapped for us, so ask for its size
    LogicalRegion my_lr = regions[0].get_logical_region();
    IndexSpace my_is = my_lr.get_index_space();
    my_bounds = runtime->get_index_space_domain(my_is).get_rect<1>();
  }

  coord_t bounds_lo = my_bounds.lo[0];
  coord_t input_size = my_bounds.volume();

  log_app.print() << "use data: subregion " << subregion_index << ", input size = " << input_size
		  << (was_virtual_mapped ? " (virtual mapped)" : " (NOT virtual mapped)");

  // check that the data is what we expect
  int errors = 0;
  {
    RegionAccessor<AccessorType::Generic, int> ra = pr.get_field_accessor(FID_DATA).typeify<int>();

    for(coord_t i = 0; i < input_size; i++) {
      int exp = (subregion_index * 10000) + i;
      int act = ra.read(DomainPoint::from_point<1>(bounds_lo + i));
      if(exp != act) {
	// don't print more than 10 errors
	if(errors < 10)
	  log_app.error() << "mismatch in subregion " << subregion_index << ": ["
			  << (bounds_lo + i) << "] = " << act << " (expected " << exp << ")";
	errors++;
      }
    }
  }

  // unmap any inline mapping we did
  if(was_virtual_mapped)
    runtime->unmap_region(ctx, pr);

  //return errors;
}

class UseDataProjectionFunctor : public ProjectionFunctor {
public:
  UseDataProjectionFunctor(void);

  virtual LogicalRegion project(Context ctx, Task *task,
				unsigned index,
				LogicalRegion upper_bound,
				const DomainPoint &point);

  virtual LogicalRegion project(Context ctx, Task *task,
				unsigned index,
				LogicalPartition upper_bound,
				const DomainPoint &point);
};

UseDataProjectionFunctor::UseDataProjectionFunctor(void) {}

// region -> region: UNUSED
LogicalRegion UseDataProjectionFunctor::project(Context ctx, Task *task,
						unsigned index,
						LogicalRegion upper_bound,
						const DomainPoint &point)
{
  assert(0);
  return LogicalRegion::NO_REGION;
}

// partition -> region path: [index].PID_ALLOCED_DATA.0
LogicalRegion UseDataProjectionFunctor::project(Context ctx, Task *task,
						unsigned index,
						LogicalPartition upper_bound,
						const DomainPoint &point)
{
  // if our application did more than one index launch using this projection functor,
  // we could consider memoizing the result of this lookup to reduce overhead on subsequent
  // launches
  LogicalRegion lr1 = runtime->get_logical_subregion_by_color(ctx, upper_bound, point);
  LogicalPartition lp1 = runtime->get_logical_partition_by_color(ctx, lr1, PID_ALLOCED_DATA);
  LogicalRegion lr2 = runtime->get_logical_subregion_by_color(ctx, lp1, 0);
  return lr2;
}

int main(int argc, char **argv)
{
  // Register our task variants
  {
    TaskVariantRegistrar top_level_registrar(TOP_LEVEL_TASK_ID);
    top_level_registrar.add_constraint(ProcessorConstraint(Processor::LOC_PROC));
    Runtime::preregister_task_variant<top_level_task>(top_level_registrar,
                                                      "Top Level Task");
    Runtime::set_top_level_task_id(TOP_LEVEL_TASK_ID);
  }
  {
    TaskVariantRegistrar make_data_registrar(MAKE_DATA_TASK_ID);
    make_data_registrar.add_constraint(ProcessorConstraint(Processor::LOC_PROC));
    Runtime::preregister_task_variant<make_data_task>(make_data_registrar,
                                                      "Make Data Task");
  }
  {
    TaskVariantRegistrar use_data_registrar(USE_DATA_TASK_ID);
    use_data_registrar.add_constraint(ProcessorConstraint(Processor::LOC_PROC));
    Runtime::preregister_task_variant<use_data_task>(use_data_registrar,
						     "Use Data Task");
  }

  Runtime::preregister_projection_functor(PFID_USE_DATA_TASK,
					  new UseDataProjectionFunctor);

  // Fire up the runtime
  return Runtime::start(argc, argv);
}
<commit_msg>virtual_map: add now-necessary get_depth method to projection functor<commit_after>/* Copyright 2016 Stanford University, NVIDIA Corporation
 *
 * 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.
 */

// This example demonstrates way in which virtual mapping can be useful -
// generating variable-sized output from a task

#include <legion.h>
#include <realm/cmdline.h>
#include <mappers/default_mapper.h>

using namespace Legion;
using namespace Legion::Mapping;
using namespace LegionRuntime::Accessor;

enum TaskID
{
  TOP_LEVEL_TASK_ID,
  MAKE_DATA_TASK_ID,
  USE_DATA_TASK_ID,
};

enum {
  FID_DATA = 55,
};

enum {
  PID_ALLOCED_DATA = 4,
};

enum {
  PFID_USE_DATA_TASK = 77,
};

LegionRuntime::Logger::Category log_app("app");

struct MakeDataTaskArgs {
  int random_seed;
  size_t average_output_size;
};

void top_level_task(const Task *task,
                    const std::vector<PhysicalRegion> &regions,
                    Context ctx, Runtime *runtime)
{
  size_t num_subregions = 4;
  size_t average_output_size = 100;
  size_t total_region_size = 1 << 30;
  int random_seed = 12345;
  bool use_projection_functor = false;

  const InputArgs &command_args = HighLevelRuntime::get_input_args();

  bool ok = Realm::CommandLineParser()
    .add_option_int("-n", num_subregions)
    .add_option_int("-s", average_output_size)
    .add_option_int("-l", total_region_size)
    .add_option_int("-seed", random_seed)
    .add_option_bool("-proj", use_projection_functor)
    .parse_command_line(command_args.argc, (const char **)command_args.argv);

  if(!ok) {
    log_app.fatal() << "error parsing command line arguments";
    exit(1);
  }

  // create a top-level region that is (easily) large enough to hold all of our
  // data - we need not worry about a tight bound, because we will never create
  // any instances of this size
  Rect<1> bounds(0, total_region_size - 1);
  IndexSpace is = runtime->create_index_space(ctx, Domain::from_rect<1>(bounds));
  runtime->attach_name(is, "is");

  // our field space will just have a single field
  FieldSpace fs = runtime->create_field_space(ctx);
  runtime->attach_name(fs, "fs");
  {
    FieldAllocator fsa = runtime->create_field_allocator(ctx, fs);
    fsa.allocate_field(sizeof(int), FID_DATA);
    runtime->attach_name(fs, FID_DATA, "data");
  }

  LogicalRegion lr = runtime->create_logical_region(ctx, is, fs);
  runtime->attach_name(lr, "lr");

  // partition it into equal pieces - again, these are expected to be much
  // larger than the actual output of the generator tasks
  Rect<1> subregion_idxs(0, num_subregions - 1);
  size_t elements_per_subregion = total_region_size / num_subregions;
  assert(elements_per_subregion >= (2 * average_output_size));
  Blockify<1> blockify(elements_per_subregion);
  IndexPartition ip = runtime->create_index_partition(ctx, is, blockify);
  LogicalPartition lp = runtime->get_logical_partition(ctx, lr, ip);

  // perform an index launch to have each subregion filled in with a variable
  // amount of data
  {
    MakeDataTaskArgs args;
    args.random_seed = random_seed;
    args.average_output_size = average_output_size;
    IndexLauncher launcher(MAKE_DATA_TASK_ID, Domain::from_rect<1>(subregion_idxs),
			   TaskArgument(&args, sizeof(args)),
			   ArgumentMap());
    launcher.add_region_requirement(RegionRequirement(lp,
						      0, // identity projection
						      READ_WRITE,
						      EXCLUSIVE,
						      lr,
						      DefaultMapper::VIRTUAL_MAP)
				    .add_field(FID_DATA));
    FutureMap fm = runtime->execute_index_space(ctx, launcher);
    fm.wait_all_results();
  }

  // now perform a second index launch to use the variable-sized chunks of
  // data
  {
    IndexLauncher launcher(USE_DATA_TASK_ID, Domain::from_rect<1>(subregion_idxs),
			   TaskArgument(0, 0),
			   ArgumentMap());
    if(use_projection_functor) {
      // use a projection functor to tell the runtime precisely which sub-subregion
      // each point task will use so that it can be mapped before the task executes -
      // make sure the default mapper doesn't try to map the parent region though
      launcher.add_region_requirement(RegionRequirement(lp,
							PFID_USE_DATA_TASK,
							READ_ONLY,
							EXCLUSIVE,
							lr,
							DefaultMapper::EXACT_REGION)
				      .add_field(FID_DATA));
    } else {
      // OR, use another virtual mapping and let the use_data_task walk the region
      // tree itself and do an inline mapping within the task
      launcher.add_region_requirement(RegionRequirement(lp,
							0, // identity projection
							READ_ONLY,
							EXCLUSIVE,
							lr,
							DefaultMapper::VIRTUAL_MAP)
				      .add_field(FID_DATA));
    }
    FutureMap fm = runtime->execute_index_space(ctx, launcher);
    fm.wait_all_results();
  }
}

void make_data_task(const Task *task,
                    const std::vector<PhysicalRegion> &regions,
                    Context ctx, Runtime *runtime)
{
  const MakeDataTaskArgs& args = *static_cast<const MakeDataTaskArgs *>(task->args);
  int subregion_index = task->index_point.get_point<1>();

  // we should _NOT_ have a mapping of our region
  assert(!regions[0].is_mapped());

  // first, decide how much data we're going to produce
  unsigned short xsubi[3];
  xsubi[0] = args.random_seed >> 16;
  xsubi[1] = args.random_seed;
  xsubi[2] = subregion_index;
  coord_t output_size = nrand48(xsubi) % (2 * args.average_output_size + 1);

  log_app.print() << "make data: subregion " << subregion_index << ", output size = " << output_size;

  // now we can define a (sub-)subregion of our subregion that is the right size
  // to hold the output
  LogicalRegion my_lr = regions[0].get_logical_region();
  IndexSpace my_is = my_lr.get_index_space();
  Rect<1> my_bounds = runtime->get_index_space_domain(my_is).get_rect<1>();
  coord_t bounds_lo = my_bounds.lo[0];

  // remember rectangle bounds are inclusive on both sides
  Rect<1> alloc_bounds(my_bounds.lo, bounds_lo + output_size - 1);

  // create a new partition with a known part_color so that other tasks can find
  // the color space will consist of the single color '0'
  DomainColoring dc;
  Domain color_space = Domain::from_rect<1>(Rect<1>(0, 0));
  dc[0] = Domain::from_rect<1>(alloc_bounds);

  IndexPartition alloc_ip = runtime->create_index_partition(ctx,
							    my_is,
							    color_space,
							    dc,
							    DISJOINT_KIND, // trivial
							    PID_ALLOCED_DATA);

  // now we get the name of the logical subregion we just created and inline map
  // it to generate our output
  LogicalPartition alloc_lp = runtime->get_logical_partition(my_lr, alloc_ip);
  IndexSpace alloc_is = runtime->get_index_subspace(alloc_ip, DomainPoint::from_point<1>(0));
  LogicalRegion alloc_lr = runtime->get_logical_subregion(alloc_lp, alloc_is);

  log_app.debug() << "created subregion " << alloc_lr;

  // tell the default mapper that we want exactly this region to be mapped
  // otherwise, its heuristics may cause it to try to map the (huge) parent
  InlineLauncher launcher(RegionRequirement(alloc_lr,
					    READ_WRITE,
					    EXCLUSIVE,
					    my_lr,
					    DefaultMapper::EXACT_REGION)
			  .add_field(FID_DATA));
  PhysicalRegion pr = runtime->map_region(ctx, launcher);

  // this would be done as part of asking for an accessor, but do it explicitly for
  // didactic purposes - while inline mappings can often be expensive due to stalls or
  // data movement, this should be fast because we are just allocating new space
  pr.wait_until_valid();

  {
    RegionAccessor<AccessorType::Generic, int> ra = pr.get_field_accessor(FID_DATA).typeify<int>();

    for(coord_t i = 0; i < output_size; i++)
      ra.write(DomainPoint::from_point<1>(bounds_lo + i),
	       (subregion_index * 10000) + i);
  }

  // release our inline mapping
  runtime->unmap_region(ctx, pr);

  // no need to explicitly return the name of the subregion we created - it can be foud
  // by walking the region tree using known colors
}

void use_data_task(const Task *task,
		   const std::vector<PhysicalRegion> &regions,
		   Context ctx, Runtime *runtime)
{
  int subregion_index = task->index_point.get_point<1>();

  PhysicalRegion pr = regions[0];
  Rect<1> my_bounds;
  bool was_virtual_mapped = !regions[0].is_mapped();
  if(was_virtual_mapped) {
    // find the actual subregion containing our input data by walking the
    // region tree
    LogicalRegion my_lr = regions[0].get_logical_region();
    IndexSpace my_is = my_lr.get_index_space();

    IndexPartition alloc_ip = runtime->get_index_partition(my_is, PID_ALLOCED_DATA);

    // getting from the index partition to the logical subregion is identical to
    // what was done in make_data_task()
    LogicalPartition alloc_lp = runtime->get_logical_partition(my_lr, alloc_ip);
    IndexSpace alloc_is = runtime->get_index_subspace(alloc_ip, DomainPoint::from_point<1>(0));
    LogicalRegion alloc_lr = runtime->get_logical_subregion(alloc_lp, alloc_is);

    log_app.debug() << "looked up subregion " << alloc_lr;

    // learn the input size from the bounds of the allocated subspace
    my_bounds = runtime->get_index_space_domain(alloc_is).get_rect<1>();

    // again, tell the default mapper that we want exactly this region to be mapped
    // this is important if the mapper sends us to a different place than where the
    // data was produced and a copy is needed
    InlineLauncher launcher(RegionRequirement(alloc_lr,
					      READ_ONLY,
					      EXCLUSIVE,
					      my_lr,
					      DefaultMapper::EXACT_REGION)
			    .add_field(FID_DATA));
    pr = runtime->map_region(ctx, launcher);
  } else {
    // we've got the exact region mapped for us, so ask for its size
    LogicalRegion my_lr = regions[0].get_logical_region();
    IndexSpace my_is = my_lr.get_index_space();
    my_bounds = runtime->get_index_space_domain(my_is).get_rect<1>();
  }

  coord_t bounds_lo = my_bounds.lo[0];
  coord_t input_size = my_bounds.volume();

  log_app.print() << "use data: subregion " << subregion_index << ", input size = " << input_size
		  << (was_virtual_mapped ? " (virtual mapped)" : " (NOT virtual mapped)");

  // check that the data is what we expect
  int errors = 0;
  {
    RegionAccessor<AccessorType::Generic, int> ra = pr.get_field_accessor(FID_DATA).typeify<int>();

    for(coord_t i = 0; i < input_size; i++) {
      int exp = (subregion_index * 10000) + i;
      int act = ra.read(DomainPoint::from_point<1>(bounds_lo + i));
      if(exp != act) {
	// don't print more than 10 errors
	if(errors < 10)
	  log_app.error() << "mismatch in subregion " << subregion_index << ": ["
			  << (bounds_lo + i) << "] = " << act << " (expected " << exp << ")";
	errors++;
      }
    }
  }

  // unmap any inline mapping we did
  if(was_virtual_mapped)
    runtime->unmap_region(ctx, pr);

  //return errors;
}

class UseDataProjectionFunctor : public ProjectionFunctor {
public:
  UseDataProjectionFunctor(void);

  virtual LogicalRegion project(Context ctx, Task *task,
				unsigned index,
				LogicalRegion upper_bound,
				const DomainPoint &point);

  virtual LogicalRegion project(Context ctx, Task *task,
				unsigned index,
				LogicalPartition upper_bound,
				const DomainPoint &point);

  virtual unsigned get_depth(void) const;
};

UseDataProjectionFunctor::UseDataProjectionFunctor(void) {}

// region -> region: UNUSED
LogicalRegion UseDataProjectionFunctor::project(Context ctx, Task *task,
						unsigned index,
						LogicalRegion upper_bound,
						const DomainPoint &point)
{
  assert(0);
  return LogicalRegion::NO_REGION;
}

// partition -> region path: [index].PID_ALLOCED_DATA.0
LogicalRegion UseDataProjectionFunctor::project(Context ctx, Task *task,
						unsigned index,
						LogicalPartition upper_bound,
						const DomainPoint &point)
{
  // if our application did more than one index launch using this projection functor,
  // we could consider memoizing the result of this lookup to reduce overhead on subsequent
  // launches
  LogicalRegion lr1 = runtime->get_logical_subregion_by_color(ctx, upper_bound, point);
  LogicalPartition lp1 = runtime->get_logical_partition_by_color(ctx, lr1, PID_ALLOCED_DATA);
  LogicalRegion lr2 = runtime->get_logical_subregion_by_color(ctx, lp1, 0);
  return lr2;
}

unsigned UseDataProjectionFunctor::get_depth(void) const
{
  return 1;
}

int main(int argc, char **argv)
{
  // Register our task variants
  {
    TaskVariantRegistrar top_level_registrar(TOP_LEVEL_TASK_ID);
    top_level_registrar.add_constraint(ProcessorConstraint(Processor::LOC_PROC));
    Runtime::preregister_task_variant<top_level_task>(top_level_registrar,
                                                      "Top Level Task");
    Runtime::set_top_level_task_id(TOP_LEVEL_TASK_ID);
  }
  {
    TaskVariantRegistrar make_data_registrar(MAKE_DATA_TASK_ID);
    make_data_registrar.add_constraint(ProcessorConstraint(Processor::LOC_PROC));
    Runtime::preregister_task_variant<make_data_task>(make_data_registrar,
                                                      "Make Data Task");
  }
  {
    TaskVariantRegistrar use_data_registrar(USE_DATA_TASK_ID);
    use_data_registrar.add_constraint(ProcessorConstraint(Processor::LOC_PROC));
    Runtime::preregister_task_variant<use_data_task>(use_data_registrar,
						     "Use Data Task");
  }

  Runtime::preregister_projection_functor(PFID_USE_DATA_TASK,
					  new UseDataProjectionFunctor);

  // Fire up the runtime
  return Runtime::start(argc, argv);
}
<|endoftext|>


<commit_before>// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.

#ifndef MFEM_DOFTRANSFORM
#define MFEM_DOFTRANSFORM

#include "../config/config.hpp"
#include "../linalg/linalg.hpp"
#include "intrules.hpp"
#include "fe.hpp"

namespace mfem
{

/** The DofTransformation class is an abstract base class for a family of
    transformations that map local degrees of freedom (DoFs), contained within
    individual elements, to global degrees of freedom, stored within
    GridFunction objects. These transformations are necessary to ensure that
    basis functions in neighboring elements align correctly. Closely related but
    complementary transformations are required for the entries stored in
    LinearForm and BilinearForm objects. The DofTransformation class is designed
    to apply the action of both of these types of DoF transformations.

    Let the "primal transformation" be given by the operator T. This means that
    given a local element vector v the data that must be placed into a
    GridFunction object is v_t = T * v.

    We also need the inverse of the primal transformation T^{-1} so that we can
    recover the local element vector from data read out of a GridFunction
    e.g. v = T^{-1} * v_t.

    We need to preserve the action of our linear forms applied to primal
    vectors. In other words, if f is the local vector computed by a linear
    form then f * v = f_t * v_t (where "*" represents an inner product of
    vectors). This requires that f_t = T^{-T} * f i.e. the "dual transform" is
    given by the transpose of the inverse of the primal transformation.

    For bilinear forms we require that v^T * A * v = v_t^T * A_t * v_t. This
    implies that A_t = T^{-T} * A * T^{-1}. This can be accomplished by
    performing dual transformations of the rows and columns of the matrix A.

    For discrete linear operators the range must be modified with the primal
    transformation rather than the dual transformation because the result is a
    primal vector rather than a dual vector. This leads to the transformation
    D_t = T * D * T^{-1}. This can be accomplished by using a primal
    transformation on the columns of D and a dual transformation on its rows.
*/
class DofTransformation
{
protected:
   int size_;

   Array<int> Fo;

   DofTransformation(int size)
      : size_(size) {}

public:

   inline int Size() const { return size_; }
   inline int Height() const { return size_; }
   inline int NumRows() const { return size_; }
   inline int Width() const { return size_; }
   inline int NumCols() const { return size_; }

   /** @brief Configure the transformation using face orientations for the
       current element. */
   /// The face_orientation array can be obtained from Mesh::GetElementFaces.
   inline void SetFaceOrientations(const Array<int> & face_orientation)
   { Fo = face_orientation; }

   inline const Array<int> & GetFaceOrientations() const { return Fo; }

   /** Transform local DoFs to align with the global DoFs. For example, this
       transformation can be used to map the local vector computed by
       FiniteElement::Project() to the transformed vector stored within a
       GridFunction object. */
   virtual void TransformPrimal(double *v) const = 0;
   virtual void TransformPrimal(Vector &v) const;

   /// Transform groups of DoFs stored as dense matrices
   virtual void TransformPrimalCols(DenseMatrix &V) const;

   /** Inverse transform local DoFs. Used to transform DoFs from a global vector
       back to their element-local form. For example, this must be used to
       transform the vector obtained using GridFunction::GetSubVector before it
       can be used to compute a local interpolation.
   */
   virtual void InvTransformPrimal(double *v) const = 0;
   virtual void InvTransformPrimal(Vector &v) const;

   /** Transform dual DoFs as computed by a LinearFormIntegrator before summing
       into a LinearForm object. */
   virtual void TransformDual(double *v) const = 0;
   virtual void TransformDual(Vector &v) const;

   /** Transform a matrix of dual DoFs entries as computed by a
       BilinearFormIntegrator before summing into a BilinearForm object. */
   virtual void TransformDual(DenseMatrix &V) const;

   /// Transform groups of dual DoFs stored as dense matrices
   virtual void TransformDualRows(DenseMatrix &V) const;
   virtual void TransformDualCols(DenseMatrix &V) const;

   virtual ~DofTransformation() {}
};

/** Transform a matrix of DoFs entries from different finite element spaces as
    computed by a DiscreteInterpolator before copying into a
    DiscreteLinearOperator.
*/
void TransformPrimal(const DofTransformation *ran_dof_trans,
                     const DofTransformation *dom_dof_trans,
                     DenseMatrix &elmat);

/** Transform a matrix of dual DoFs entries from different finite element spaces
    as computed by a BilinearFormIntegrator before summing into a
    MixedBilinearForm object.
*/
void TransformDual(const DofTransformation *ran_dof_trans,
                   const DofTransformation *dom_dof_trans,
                   DenseMatrix &elmat);

/** The VDofTransformation class implements a nested transformation where an
    arbitrary DofTransformation is replicated with a vdim >= 1.
*/
class VDofTransformation : public DofTransformation
{
private:
   int vdim_;
   int ordering_;
   DofTransformation * doftrans_;

public:
   /** @brief Default constructor which requires that SetDofTransformation be
       called before use. */
   VDofTransformation(int vdim = 1, int ordering = 0)
      : DofTransformation(0),
        vdim_(vdim), ordering_(ordering),
        doftrans_(NULL) {}

   /// Constructor with a known DofTransformation
   VDofTransformation(DofTransformation & doftrans, int vdim = 1,
                      int ordering = 0)
      : DofTransformation(vdim * doftrans.Size()),
        vdim_(vdim), ordering_(ordering),
        doftrans_(&doftrans) {}

   /// Set or change the vdim parameter
   inline void SetVDim(int vdim)
   {
      vdim_ = vdim;
      if (doftrans_)
      {
         size_ = vdim_ * doftrans_->Size();
      }
   }

   /// Return the current vdim value
   inline int GetVDim() const { return vdim_; }

   /// Set or change the nested DofTransformation object
   inline void SetDofTransformation(DofTransformation & doftrans)
   {
      size_ = vdim_ * doftrans.Size();
      doftrans_ = &doftrans;
   }

   /// Return the nested DofTransformation object
   inline DofTransformation * GetDofTransformation() const { return doftrans_; }

   inline void SetFaceOrientation(const Array<int> & face_orientation)
   { Fo = face_orientation; doftrans_->SetFaceOrientations(face_orientation); }

   using DofTransformation::TransformPrimal;
   using DofTransformation::InvTransformPrimal;
   using DofTransformation::TransformDual;

   void TransformPrimal(double *v) const;
   void InvTransformPrimal(double *v) const;
   void TransformDual(double *v) const;
};

/** Abstract base class for high-order Nedelec spaces on elements with
    triangular faces.

    The Nedelec DoFs on the interior of triangular faces come in pairs which
    share an interpolation point but have different vector directions. These
    directions depend on the orientation of the face and can therefore differ in
    neighboring elements. The mapping required to transform these DoFs can be
    implemented as series of 2x2 linear transformations. The raw data for these
    linear transformations is stored in the T_data and TInv_data arrays and can
    be accessed as DenseMatrices using the GetFaceTransform() and
    GetFaceInverseTransform() methods.
*/
class ND_DofTransformation : public DofTransformation
{
protected:
   static const double T_data[24];
   static const double TInv_data[24];
   static const DenseTensor T, TInv;
   int order;

   ND_DofTransformation(int size, int order);

public:
   // Return the 2x2 transformation operator for the given face orientation
   static const DenseMatrix & GetFaceTransform(int ori) { return T(ori); }

   // Return the 2x2 inverse transformation operator
   static const DenseMatrix & GetFaceInverseTransform(int ori)
   { return TInv(ori); }
};

/// DoF transformation implementation for the Nedelec basis on triangles
class ND_TriDofTransformation : public ND_DofTransformation
{
public:
   ND_TriDofTransformation(int order);

   using DofTransformation::TransformPrimal;
   using DofTransformation::InvTransformPrimal;
   using DofTransformation::TransformDual;

   void TransformPrimal(double *v) const;

   void InvTransformPrimal(double *v) const;

   void TransformDual(double *v) const;
};

/// DoF transformation implementation for the Nedelec basis on tetrahedra
class ND_TetDofTransformation : public ND_DofTransformation
{
public:
   ND_TetDofTransformation(int order);

   using DofTransformation::TransformPrimal;
   using DofTransformation::InvTransformPrimal;
   using DofTransformation::TransformDual;

   void TransformPrimal(double *v) const;

   void InvTransformPrimal(double *v) const;

   void TransformDual(double *v) const;
};

/// DoF transformation implementation for the Nedelec basis on wedge elements
/** TODO: Implementation in the nd-prism-dev branch */
class ND_WedgeDofTransformation : public ND_DofTransformation
{
public:
   ND_WedgeDofTransformation(int order);

   using DofTransformation::TransformPrimal;
   using DofTransformation::InvTransformPrimal;
   using DofTransformation::TransformDual;

   void TransformPrimal(double *v) const;

   void InvTransformPrimal(double *v) const;

   void TransformDual(double *v) const;
};

} // namespace mfem

#endif // MFEM_DOFTRANSFORM
<commit_msg>Adjusting comment<commit_after>// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.

#ifndef MFEM_DOFTRANSFORM
#define MFEM_DOFTRANSFORM

#include "../config/config.hpp"
#include "../linalg/linalg.hpp"
#include "intrules.hpp"
#include "fe.hpp"

namespace mfem
{

/** The DofTransformation class is an abstract base class for a family of
    transformations that map local degrees of freedom (DoFs), contained within
    individual elements, to global degrees of freedom, stored within
    GridFunction objects. These transformations are necessary to ensure that
    basis functions in neighboring elements align correctly. Closely related but
    complementary transformations are required for the entries stored in
    LinearForm and BilinearForm objects. The DofTransformation class is designed
    to apply the action of both of these types of DoF transformations.

    Let the "primal transformation" be given by the operator T. This means that
    given a local element vector v the data that must be placed into a
    GridFunction object is v_t = T * v.

    We also need the inverse of the primal transformation T^{-1} so that we can
    recover the local element vector from data read out of a GridFunction
    e.g. v = T^{-1} * v_t.

    We need to preserve the action of our linear forms applied to primal
    vectors. In other words, if f is the local vector computed by a linear
    form then f * v = f_t * v_t (where "*" represents an inner product of
    vectors). This requires that f_t = T^{-T} * f i.e. the "dual transform" is
    given by the transpose of the inverse of the primal transformation.

    For bilinear forms we require that v^T * A * v = v_t^T * A_t * v_t. This
    implies that A_t = T^{-T} * A * T^{-1}. This can be accomplished by
    performing dual transformations of the rows and columns of the matrix A.

    For discrete linear operators the range must be modified with the primal
    transformation rather than the dual transformation because the result is a
    primal vector rather than a dual vector. This leads to the transformation
    D_t = T * D * T^{-1}. This can be accomplished by using a primal
    transformation on the columns of D and a dual transformation on its rows.
*/
class DofTransformation
{
protected:
   int size_;

   Array<int> Fo;

   DofTransformation(int size)
      : size_(size) {}

public:

   inline int Size() const { return size_; }
   inline int Height() const { return size_; }
   inline int NumRows() const { return size_; }
   inline int Width() const { return size_; }
   inline int NumCols() const { return size_; }

   /** @brief Configure the transformation using face orientations for the
       current element. */
   /// The face_orientation array can be obtained from Mesh::GetElementFaces.
   inline void SetFaceOrientations(const Array<int> & face_orientation)
   { Fo = face_orientation; }

   inline const Array<int> & GetFaceOrientations() const { return Fo; }

   /** Transform local DoFs to align with the global DoFs. For example, this
       transformation can be used to map the local vector computed by
       FiniteElement::Project() to the transformed vector stored within a
       GridFunction object. */
   virtual void TransformPrimal(double *v) const = 0;
   virtual void TransformPrimal(Vector &v) const;

   /// Transform groups of DoFs stored as dense matrices
   virtual void TransformPrimalCols(DenseMatrix &V) const;

   /** Inverse transform local DoFs. Used to transform DoFs from a global vector
       back to their element-local form. For example, this must be used to
       transform the vector obtained using GridFunction::GetSubVector before it
       can be used to compute a local interpolation.
   */
   virtual void InvTransformPrimal(double *v) const = 0;
   virtual void InvTransformPrimal(Vector &v) const;

   /** Transform dual DoFs as computed by a LinearFormIntegrator before summing
       into a LinearForm object. */
   virtual void TransformDual(double *v) const = 0;
   virtual void TransformDual(Vector &v) const;

   /** Transform a matrix of dual DoFs entries as computed by a
       BilinearFormIntegrator before summing into a BilinearForm object. */
   virtual void TransformDual(DenseMatrix &V) const;

   /// Transform groups of dual DoFs stored as dense matrices
   virtual void TransformDualRows(DenseMatrix &V) const;
   virtual void TransformDualCols(DenseMatrix &V) const;

   virtual ~DofTransformation() {}
};

/** Transform a matrix of DoFs entries from different finite element spaces as
    computed by a DiscreteInterpolator before copying into a
    DiscreteLinearOperator.
*/
void TransformPrimal(const DofTransformation *ran_dof_trans,
                     const DofTransformation *dom_dof_trans,
                     DenseMatrix &elmat);

/** Transform a matrix of dual DoFs entries from different finite element spaces
    as computed by a BilinearFormIntegrator before summing into a
    MixedBilinearForm object.
*/
void TransformDual(const DofTransformation *ran_dof_trans,
                   const DofTransformation *dom_dof_trans,
                   DenseMatrix &elmat);

/** The VDofTransformation class implements a nested transformation where an
    arbitrary DofTransformation is replicated with a vdim >= 1.
*/
class VDofTransformation : public DofTransformation
{
private:
   int vdim_;
   int ordering_;
   DofTransformation * doftrans_;

public:
   /** @brief Default constructor which requires that SetDofTransformation be
       called before use. */
   VDofTransformation(int vdim = 1, int ordering = 0)
      : DofTransformation(0),
        vdim_(vdim), ordering_(ordering),
        doftrans_(NULL) {}

   /// Constructor with a known DofTransformation
   VDofTransformation(DofTransformation & doftrans, int vdim = 1,
                      int ordering = 0)
      : DofTransformation(vdim * doftrans.Size()),
        vdim_(vdim), ordering_(ordering),
        doftrans_(&doftrans) {}

   /// Set or change the vdim parameter
   inline void SetVDim(int vdim)
   {
      vdim_ = vdim;
      if (doftrans_)
      {
         size_ = vdim_ * doftrans_->Size();
      }
   }

   /// Return the current vdim value
   inline int GetVDim() const { return vdim_; }

   /// Set or change the nested DofTransformation object
   inline void SetDofTransformation(DofTransformation & doftrans)
   {
      size_ = vdim_ * doftrans.Size();
      doftrans_ = &doftrans;
   }

   /// Return the nested DofTransformation object
   inline DofTransformation * GetDofTransformation() const { return doftrans_; }

   inline void SetFaceOrientation(const Array<int> & face_orientation)
   { Fo = face_orientation; doftrans_->SetFaceOrientations(face_orientation); }

   using DofTransformation::TransformPrimal;
   using DofTransformation::InvTransformPrimal;
   using DofTransformation::TransformDual;

   void TransformPrimal(double *v) const;
   void InvTransformPrimal(double *v) const;
   void TransformDual(double *v) const;
};

/** Abstract base class for high-order Nedelec spaces on elements with
    triangular faces.

    The Nedelec DoFs on the interior of triangular faces come in pairs which
    share an interpolation point but have different vector directions. These
    directions depend on the orientation of the face and can therefore differ in
    neighboring elements. The mapping required to transform these DoFs can be
    implemented as series of 2x2 linear transformations. The raw data for these
    linear transformations is stored in the T_data and TInv_data arrays and can
    be accessed as DenseMatrices using the GetFaceTransform() and
    GetFaceInverseTransform() methods.
*/
class ND_DofTransformation : public DofTransformation
{
protected:
   static const double T_data[24];
   static const double TInv_data[24];
   static const DenseTensor T, TInv;
   int order;

   ND_DofTransformation(int size, int order);

public:
   // Return the 2x2 transformation operator for the given face orientation
   static const DenseMatrix & GetFaceTransform(int ori) { return T(ori); }

   // Return the 2x2 inverse transformation operator
   static const DenseMatrix & GetFaceInverseTransform(int ori)
   { return TInv(ori); }
};

/// DoF transformation implementation for the Nedelec basis on triangles
class ND_TriDofTransformation : public ND_DofTransformation
{
public:
   ND_TriDofTransformation(int order);

   using DofTransformation::TransformPrimal;
   using DofTransformation::InvTransformPrimal;
   using DofTransformation::TransformDual;

   void TransformPrimal(double *v) const;

   void InvTransformPrimal(double *v) const;

   void TransformDual(double *v) const;
};

/// DoF transformation implementation for the Nedelec basis on tetrahedra
class ND_TetDofTransformation : public ND_DofTransformation
{
public:
   ND_TetDofTransformation(int order);

   using DofTransformation::TransformPrimal;
   using DofTransformation::InvTransformPrimal;
   using DofTransformation::TransformDual;

   void TransformPrimal(double *v) const;

   void InvTransformPrimal(double *v) const;

   void TransformDual(double *v) const;
};

/// DoF transformation implementation for the Nedelec basis on wedge elements
/** TODO: (Under development) */
class ND_WedgeDofTransformation : public ND_DofTransformation
{
public:
   ND_WedgeDofTransformation(int order);

   using DofTransformation::TransformPrimal;
   using DofTransformation::InvTransformPrimal;
   using DofTransformation::TransformDual;

   void TransformPrimal(double *v) const;

   void InvTransformPrimal(double *v) const;

   void TransformDual(double *v) const;
};

} // namespace mfem

#endif // MFEM_DOFTRANSFORM
<|endoftext|>