<commit_before>/*
 * Copyright (c) 2017, Matias Fontanini
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above
 *   copyright notice, this list of conditions and the following disclaimer
 *   in the documentation and/or other materials provided with the
 *   distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <stdexcept>
#include <vector>
#include <cstring>
#include <iterator>
#include "macros.h"
#ifndef _WIN32
    #include <netinet/in.h>
    #if defined(BSD) || defined(__FreeBSD_kernel__)
        #include <ifaddrs.h>
        #include <net/if_dl.h>
        #include <sys/socket.h>
    #else
        #include <linux/if_packet.h>
    #endif
    #include <ifaddrs.h>
    #include <net/if.h>
#else
    #include <winsock2.h>
    #include <ws2tcpip.h>
    #include <iphlpapi.h>
    #undef interface
#endif
#include "network_interface.h"
#include "endianness.h"
#include "exceptions.h"
#include "utils/routing_utils.h"

using std::string;
using std::wstring;
using std::vector;
using std::set;
using std::copy;

/** \cond */
struct InterfaceInfoCollector {
    typedef Tins::NetworkInterface::Info info_type;
    info_type* info;
    int iface_id;
    const char* iface_name;
    bool found_hw;
    bool found_ip;

    InterfaceInfoCollector(info_type* res, int id, const char* if_name)
    : info(res), iface_id(id), iface_name(if_name), found_hw(false), found_ip(false) { }

    #ifndef _WIN32
    bool operator() (const struct ifaddrs* addr) {
        using Tins::Endian::host_to_be;
        using Tins::IPv4Address;
        #if defined(BSD) || defined(__FreeBSD_kernel__)
            #define TINS_BROADCAST_ADDR(addr) (addr->ifa_dstaddr)
            const struct sockaddr_dl* addr_ptr = ((struct sockaddr_dl*)addr->ifa_addr);

            if (addr->ifa_addr->sa_family == AF_LINK && addr_ptr->sdl_index == iface_id) {
                info->hw_addr = (const uint8_t*)LLADDR(addr_ptr);
                found_hw = true;
                info->is_up = info->is_up || (addr->ifa_flags & IFF_UP);
            }
        #else
            #define TINS_BROADCAST_ADDR(addr) (addr->ifa_broadaddr)
            const struct sockaddr_ll* addr_ptr = ((struct sockaddr_ll*)addr->ifa_addr);

            if (!addr->ifa_addr) {
                return false;
            }
            if (addr->ifa_addr->sa_family == AF_PACKET && addr_ptr->sll_ifindex == iface_id) {
                info->hw_addr = addr_ptr->sll_addr;
                found_hw = true;
                info->is_up = info->is_up || (addr->ifa_flags & IFF_UP);
            }
        #endif
            else if (!std::strcmp(addr->ifa_name, iface_name)) {
                if (addr->ifa_addr->sa_family == AF_INET) {
                    info->ip_addr = IPv4Address(((struct sockaddr_in *)addr->ifa_addr)->sin_addr.s_addr);
                    info->netmask = IPv4Address(((struct sockaddr_in *)addr->ifa_netmask)->sin_addr.s_addr);
                    if ((addr->ifa_flags & (IFF_BROADCAST | IFF_POINTOPOINT))) {
                        info->bcast_addr = IPv4Address(
                            ((struct sockaddr_in *)TINS_BROADCAST_ADDR(addr))->sin_addr.s_addr);
                    }
                    else {
                        info->bcast_addr = 0;
                    }
                    found_ip = true;
                }
                else if (addr->ifa_addr->sa_family == AF_INET6) {
                    Tins::NetworkInterface::IPv6Prefix prefix;
                    prefix.address = ((struct sockaddr_in6 *)addr->ifa_addr)->sin6_addr.s6_addr;
                    Tins::IPv6Address mask = ((struct sockaddr_in6 *)addr->ifa_netmask)->sin6_addr.s6_addr;
                    prefix.prefix_length = 0;
                    for (Tins::IPv6Address::iterator iter = mask.begin(); iter != mask.end(); ++iter) {
                        if (*iter == 255) {
                            prefix.prefix_length += 8;
                        }
                        else {
                            uint8_t current_value = 128;
                            while (*iter > 0) {
                                prefix.prefix_length += 1;
                                *iter &= ~current_value;
                                current_value /= 2;
                            }
                            break;
                        }
                    }
                    info->ipv6_addrs.push_back(prefix);
                }
            }
        #undef TINS_BROADCAST_ADDR
        return found_ip && found_hw;
    }
    #else // _WIN32
    bool operator() (const IP_ADAPTER_ADDRESSES* iface) {
        using Tins::IPv4Address;
        using Tins::Endian::host_to_be;
        if (iface_id == uint32_t(iface->IfIndex)) {
            copy(iface->PhysicalAddress, iface->PhysicalAddress + 6, info->hw_addr.begin());
            found_hw = true;
            info->is_up = (iface->OperStatus == IfOperStatusUp);
            IP_ADAPTER_UNICAST_ADDRESS* unicast = iface->FirstUnicastAddress;
            while (unicast) {
                int family = ((const struct sockaddr*)unicast->Address.lpSockaddr)->sa_family;
                if (family == AF_INET) {
                    info->ip_addr = IPv4Address(((const struct sockaddr_in *)unicast->Address.lpSockaddr)->sin_addr.s_addr);
                    info->netmask = IPv4Address(host_to_be<uint32_t>(0xffffffff << (32 - unicast->OnLinkPrefixLength)));
                    info->bcast_addr = IPv4Address((info->ip_addr & info->netmask) | ~info->netmask);
                    found_ip = true;
                }
                else if (family == AF_INET6) {
                    Tins::NetworkInterface::IPv6Prefix prefix;
                    prefix.address = ((const struct sockaddr_in6 *)unicast->Address.lpSockaddr)->sin6_addr.s6_addr;
                    prefix.prefix_length = unicast->OnLinkPrefixLength;
                    info->ipv6_addrs.push_back(prefix);
                    found_ip = true;
                }
                unicast = unicast->Next;
            }
        }
        return found_ip && found_hw;
    }
    #endif // _WIN32
};

#ifdef _WIN32
template <typename T, typename U>
T find_adapter_address_info(uint32_t iface_id, U (IP_ADAPTER_ADDRESSES::*member)) {
    ULONG size;
    ::GetAdaptersAddresses(AF_INET, 0, 0, 0, &size);
    vector<uint8_t> buffer(size);
    if (::GetAdaptersAddresses(AF_INET, 0, 0, (IP_ADAPTER_ADDRESSES *)&buffer[0], &size) == ERROR_SUCCESS) {
        PIP_ADAPTER_ADDRESSES iface = (IP_ADAPTER_ADDRESSES *)&buffer[0];
        while (iface) {
            if (iface->IfIndex == iface_id) {
                return T(iface->*member);
            }
            iface = iface->Next;
        }
    }
    throw Tins::invalid_interface();
}
#endif // _WIN32

/** \endcond */

namespace Tins {

// static
NetworkInterface NetworkInterface::default_interface() {
    return NetworkInterface(IPv4Address(uint32_t(0)));
}

vector<NetworkInterface> NetworkInterface::all() {
    const set<string> interfaces = Utils::network_interfaces();
    vector<NetworkInterface> output;
    for (set<string>::const_iterator it = interfaces.begin(); it != interfaces.end(); ++it) {
        output.push_back(*it);
    }
    return output;
}

NetworkInterface::NetworkInterface()
: iface_id_(0) {

}

NetworkInterface NetworkInterface::from_index(id_type identifier) {
    NetworkInterface iface;
    iface.iface_id_ = identifier;
    return iface;
}

NetworkInterface::NetworkInterface(const char* name) {
    iface_id_ = name ? resolve_index(name) : 0;
}

NetworkInterface::NetworkInterface(const std::string& name) {
    iface_id_ = resolve_index(name.c_str());
}

NetworkInterface::NetworkInterface(IPv4Address ip)
: iface_id_(0) {
    typedef vector<Utils::RouteEntry> entries_type;

    if (ip == "127.0.0.1") {
        #if defined(BSD) || defined(__FreeBSD_kernel__)
        iface_id_ = resolve_index("lo0");
        #else
        iface_id_ = resolve_index("lo");
        #endif
    }
    else {
        const Utils::RouteEntry* best_match = 0;
        entries_type entries;
        uint32_t ip_int = ip;
        Utils::route_entries(std::back_inserter(entries));
        for (entries_type::const_iterator it(entries.begin()); it != entries.end(); ++it) {
            if ((ip_int & it->mask) == it->destination) {
                if (!best_match || it->mask > best_match->mask || it->metric < best_match->metric) {
                    best_match = &*it;
                }
            }
        }
        if (!best_match) {
            throw invalid_interface();
        }
        iface_id_ = resolve_index(best_match->interface.c_str());
    }
}

string NetworkInterface::name() const {
    #ifndef _WIN32
    char iface_name[IF_NAMESIZE];
    if (!if_indextoname(iface_id_, iface_name)) {
        throw invalid_interface();
    }
    return iface_name;
    #else // _WIN32
    return find_adapter_address_info<string>(iface_id_, &IP_ADAPTER_ADDRESSES::AdapterName);
    #endif // WIN32
}

wstring NetworkInterface::friendly_name() const {
    #ifndef _WIN32
    string n = name();
    return wstring(n.begin(), n.end());
    #else // _WIN32
    return find_adapter_address_info<wstring>(iface_id_, &IP_ADAPTER_ADDRESSES::FriendlyName);
    #endif // WIN32
}

NetworkInterface::Info NetworkInterface::addresses() const {
    return info();
}

NetworkInterface::Info NetworkInterface::info() const {
    const std::string& iface_name = name();
    Info info;
    InterfaceInfoCollector collector(&info, iface_id_, iface_name.c_str());
    info.is_up = false;

    #ifdef _WIN32

    ULONG size;
    ::GetAdaptersAddresses(AF_INET, 0, 0, 0, &size);
    std::vector<uint8_t> buffer(size);
    if (::GetAdaptersAddresses(AF_INET, 0, 0, (IP_ADAPTER_ADDRESSES *)&buffer[0], &size) == ERROR_SUCCESS) {
        PIP_ADAPTER_ADDRESSES iface = (IP_ADAPTER_ADDRESSES *)&buffer[0];
        while (iface) {
            collector(iface);
            iface = iface->Next;
        }
    }

    #else // _WIN32

    struct ifaddrs* ifaddrs = 0;
    struct ifaddrs* if_it = 0;
    getifaddrs(&ifaddrs);
    for (if_it = ifaddrs; if_it; if_it = if_it->ifa_next) {
        collector(if_it);
    }
    if (ifaddrs) {
        freeifaddrs(ifaddrs);
    }

    #endif // _WIN32

     // If we didn't even get the hw address or ip address, this went wrong
    if (!collector.found_hw && !collector.found_ip) {
        throw invalid_interface();
    }

    return info;
}

bool NetworkInterface::is_loopback() const {
    return info().ip_addr.is_loopback();
}

bool NetworkInterface::is_up() const {
    return info().is_up;
}

NetworkInterface::address_type NetworkInterface::hw_address() const {
    return info().hw_addr;
}

IPv4Address NetworkInterface::ipv4_address() const {
    return info().ip_addr;
}

IPv4Address NetworkInterface::ipv4_mask() const {
    return info().netmask;
}

IPv4Address NetworkInterface::ipv4_broadcast() const {
    return info().bcast_addr;
}

vector<NetworkInterface::IPv6Prefix> NetworkInterface::ipv6_addresses() const {
    return info().ipv6_addrs;
}

NetworkInterface::id_type NetworkInterface::resolve_index(const char* name) {
    #ifndef _WIN32
    id_type id = if_nametoindex(name);
    if (!id) {
        throw invalid_interface();
    }
    return id;
    #else // _WIN32
    ULONG size;
    ::GetAdaptersAddresses(AF_INET, 0, 0, 0, &size);
    vector<uint8_t> buffer(size);
    if (::GetAdaptersAddresses(AF_INET, 0, 0, (IP_ADAPTER_ADDRESSES *)&buffer[0], &size) == ERROR_SUCCESS) {
        PIP_ADAPTER_ADDRESSES iface = (IP_ADAPTER_ADDRESSES *)&buffer[0];
        while (iface) {
            if (strcmp(iface->AdapterName, name) == 0) {
                return iface->IfIndex;
            }
            iface = iface->Next;
        }
    }
    throw invalid_interface();
    #endif // _WIN32
}

} // Tins
<commit_msg>Only use IFF_POINTOPOINT on BSD when getting broadcast address<commit_after>/*
 * Copyright (c) 2017, Matias Fontanini
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above
 *   copyright notice, this list of conditions and the following disclaimer
 *   in the documentation and/or other materials provided with the
 *   distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <stdexcept>
#include <vector>
#include <cstring>
#include <iterator>
#include "macros.h"
#ifndef _WIN32
    #include <netinet/in.h>
    #if defined(BSD) || defined(__FreeBSD_kernel__)
        #include <ifaddrs.h>
        #include <net/if_dl.h>
        #include <sys/socket.h>
    #else
        #include <linux/if_packet.h>
    #endif
    #include <ifaddrs.h>
    #include <net/if.h>
#else
    #include <winsock2.h>
    #include <ws2tcpip.h>
    #include <iphlpapi.h>
    #undef interface
#endif
#include "network_interface.h"
#include "endianness.h"
#include "exceptions.h"
#include "utils/routing_utils.h"

using std::string;
using std::wstring;
using std::vector;
using std::set;
using std::copy;

/** \cond */
struct InterfaceInfoCollector {
    typedef Tins::NetworkInterface::Info info_type;
    info_type* info;
    int iface_id;
    const char* iface_name;
    bool found_hw;
    bool found_ip;

    InterfaceInfoCollector(info_type* res, int id, const char* if_name)
    : info(res), iface_id(id), iface_name(if_name), found_hw(false), found_ip(false) { }

    #ifndef _WIN32
    bool operator() (const struct ifaddrs* addr) {
        using Tins::Endian::host_to_be;
        using Tins::IPv4Address;
        #if defined(BSD) || defined(__FreeBSD_kernel__)
            #define TINS_BROADCAST_ADDR(addr) (addr->ifa_dstaddr)
            #define TINS_BROADCAST_FLAGS (IFF_BROADCAST | IFF_POINTOPOINT)
            const struct sockaddr_dl* addr_ptr = ((struct sockaddr_dl*)addr->ifa_addr);

            if (addr->ifa_addr->sa_family == AF_LINK && addr_ptr->sdl_index == iface_id) {
                info->hw_addr = (const uint8_t*)LLADDR(addr_ptr);
                found_hw = true;
                info->is_up = info->is_up || (addr->ifa_flags & IFF_UP);
            }
        #else
            #define TINS_BROADCAST_ADDR(addr) (addr->ifa_broadaddr)
            #define TINS_BROADCAST_FLAGS (IFF_BROADCAST)
            const struct sockaddr_ll* addr_ptr = ((struct sockaddr_ll*)addr->ifa_addr);

            if (!addr->ifa_addr) {
                return false;
            }
            if (addr->ifa_addr->sa_family == AF_PACKET && addr_ptr->sll_ifindex == iface_id) {
                info->hw_addr = addr_ptr->sll_addr;
                found_hw = true;
                info->is_up = info->is_up || (addr->ifa_flags & IFF_UP);
            }
        #endif
            else if (!std::strcmp(addr->ifa_name, iface_name)) {
                if (addr->ifa_addr->sa_family == AF_INET) {
                    info->ip_addr = IPv4Address(((struct sockaddr_in *)addr->ifa_addr)->sin_addr.s_addr);
                    info->netmask = IPv4Address(((struct sockaddr_in *)addr->ifa_netmask)->sin_addr.s_addr);
                    if ((addr->ifa_flags & (TINS_BROADCAST_FLAGS))) {
                        info->bcast_addr = IPv4Address(
                            ((struct sockaddr_in *)TINS_BROADCAST_ADDR(addr))->sin_addr.s_addr);
                    }
                    else {
                        info->bcast_addr = 0;
                    }
                    found_ip = true;
                }
                else if (addr->ifa_addr->sa_family == AF_INET6) {
                    Tins::NetworkInterface::IPv6Prefix prefix;
                    prefix.address = ((struct sockaddr_in6 *)addr->ifa_addr)->sin6_addr.s6_addr;
                    Tins::IPv6Address mask = ((struct sockaddr_in6 *)addr->ifa_netmask)->sin6_addr.s6_addr;
                    prefix.prefix_length = 0;
                    for (Tins::IPv6Address::iterator iter = mask.begin(); iter != mask.end(); ++iter) {
                        if (*iter == 255) {
                            prefix.prefix_length += 8;
                        }
                        else {
                            uint8_t current_value = 128;
                            while (*iter > 0) {
                                prefix.prefix_length += 1;
                                *iter &= ~current_value;
                                current_value /= 2;
                            }
                            break;
                        }
                    }
                    info->ipv6_addrs.push_back(prefix);
                }
            }
        #undef TINS_BROADCAST_ADDR
        #undef TINS_BROADCAST_FLAGS
        return found_ip && found_hw;
    }
    #else // _WIN32
    bool operator() (const IP_ADAPTER_ADDRESSES* iface) {
        using Tins::IPv4Address;
        using Tins::Endian::host_to_be;
        if (iface_id == uint32_t(iface->IfIndex)) {
            copy(iface->PhysicalAddress, iface->PhysicalAddress + 6, info->hw_addr.begin());
            found_hw = true;
            info->is_up = (iface->OperStatus == IfOperStatusUp);
            IP_ADAPTER_UNICAST_ADDRESS* unicast = iface->FirstUnicastAddress;
            while (unicast) {
                int family = ((const struct sockaddr*)unicast->Address.lpSockaddr)->sa_family;
                if (family == AF_INET) {
                    info->ip_addr = IPv4Address(((const struct sockaddr_in *)unicast->Address.lpSockaddr)->sin_addr.s_addr);
                    info->netmask = IPv4Address(host_to_be<uint32_t>(0xffffffff << (32 - unicast->OnLinkPrefixLength)));
                    info->bcast_addr = IPv4Address((info->ip_addr & info->netmask) | ~info->netmask);
                    found_ip = true;
                }
                else if (family == AF_INET6) {
                    Tins::NetworkInterface::IPv6Prefix prefix;
                    prefix.address = ((const struct sockaddr_in6 *)unicast->Address.lpSockaddr)->sin6_addr.s6_addr;
                    prefix.prefix_length = unicast->OnLinkPrefixLength;
                    info->ipv6_addrs.push_back(prefix);
                    found_ip = true;
                }
                unicast = unicast->Next;
            }
        }
        return found_ip && found_hw;
    }
    #endif // _WIN32
};

#ifdef _WIN32
template <typename T, typename U>
T find_adapter_address_info(uint32_t iface_id, U (IP_ADAPTER_ADDRESSES::*member)) {
    ULONG size;
    ::GetAdaptersAddresses(AF_INET, 0, 0, 0, &size);
    vector<uint8_t> buffer(size);
    if (::GetAdaptersAddresses(AF_INET, 0, 0, (IP_ADAPTER_ADDRESSES *)&buffer[0], &size) == ERROR_SUCCESS) {
        PIP_ADAPTER_ADDRESSES iface = (IP_ADAPTER_ADDRESSES *)&buffer[0];
        while (iface) {
            if (iface->IfIndex == iface_id) {
                return T(iface->*member);
            }
            iface = iface->Next;
        }
    }
    throw Tins::invalid_interface();
}
#endif // _WIN32

/** \endcond */

namespace Tins {

// static
NetworkInterface NetworkInterface::default_interface() {
    return NetworkInterface(IPv4Address(uint32_t(0)));
}

vector<NetworkInterface> NetworkInterface::all() {
    const set<string> interfaces = Utils::network_interfaces();
    vector<NetworkInterface> output;
    for (set<string>::const_iterator it = interfaces.begin(); it != interfaces.end(); ++it) {
        output.push_back(*it);
    }
    return output;
}

NetworkInterface::NetworkInterface()
: iface_id_(0) {

}

NetworkInterface NetworkInterface::from_index(id_type identifier) {
    NetworkInterface iface;
    iface.iface_id_ = identifier;
    return iface;
}

NetworkInterface::NetworkInterface(const char* name) {
    iface_id_ = name ? resolve_index(name) : 0;
}

NetworkInterface::NetworkInterface(const std::string& name) {
    iface_id_ = resolve_index(name.c_str());
}

NetworkInterface::NetworkInterface(IPv4Address ip)
: iface_id_(0) {
    typedef vector<Utils::RouteEntry> entries_type;

    if (ip == "127.0.0.1") {
        #if defined(BSD) || defined(__FreeBSD_kernel__)
        iface_id_ = resolve_index("lo0");
        #else
        iface_id_ = resolve_index("lo");
        #endif
    }
    else {
        const Utils::RouteEntry* best_match = 0;
        entries_type entries;
        uint32_t ip_int = ip;
        Utils::route_entries(std::back_inserter(entries));
        for (entries_type::const_iterator it(entries.begin()); it != entries.end(); ++it) {
            if ((ip_int & it->mask) == it->destination) {
                if (!best_match || it->mask > best_match->mask || it->metric < best_match->metric) {
                    best_match = &*it;
                }
            }
        }
        if (!best_match) {
            throw invalid_interface();
        }
        iface_id_ = resolve_index(best_match->interface.c_str());
    }
}

string NetworkInterface::name() const {
    #ifndef _WIN32
    char iface_name[IF_NAMESIZE];
    if (!if_indextoname(iface_id_, iface_name)) {
        throw invalid_interface();
    }
    return iface_name;
    #else // _WIN32
    return find_adapter_address_info<string>(iface_id_, &IP_ADAPTER_ADDRESSES::AdapterName);
    #endif // WIN32
}

wstring NetworkInterface::friendly_name() const {
    #ifndef _WIN32
    string n = name();
    return wstring(n.begin(), n.end());
    #else // _WIN32
    return find_adapter_address_info<wstring>(iface_id_, &IP_ADAPTER_ADDRESSES::FriendlyName);
    #endif // WIN32
}

NetworkInterface::Info NetworkInterface::addresses() const {
    return info();
}

NetworkInterface::Info NetworkInterface::info() const {
    const std::string& iface_name = name();
    Info info;
    InterfaceInfoCollector collector(&info, iface_id_, iface_name.c_str());
    info.is_up = false;

    #ifdef _WIN32

    ULONG size;
    ::GetAdaptersAddresses(AF_INET, 0, 0, 0, &size);
    std::vector<uint8_t> buffer(size);
    if (::GetAdaptersAddresses(AF_INET, 0, 0, (IP_ADAPTER_ADDRESSES *)&buffer[0], &size) == ERROR_SUCCESS) {
        PIP_ADAPTER_ADDRESSES iface = (IP_ADAPTER_ADDRESSES *)&buffer[0];
        while (iface) {
            collector(iface);
            iface = iface->Next;
        }
    }

    #else // _WIN32

    struct ifaddrs* ifaddrs = 0;
    struct ifaddrs* if_it = 0;
    getifaddrs(&ifaddrs);
    for (if_it = ifaddrs; if_it; if_it = if_it->ifa_next) {
        collector(if_it);
    }
    if (ifaddrs) {
        freeifaddrs(ifaddrs);
    }

    #endif // _WIN32

     // If we didn't even get the hw address or ip address, this went wrong
    if (!collector.found_hw && !collector.found_ip) {
        throw invalid_interface();
    }

    return info;
}

bool NetworkInterface::is_loopback() const {
    return info().ip_addr.is_loopback();
}

bool NetworkInterface::is_up() const {
    return info().is_up;
}

NetworkInterface::address_type NetworkInterface::hw_address() const {
    return info().hw_addr;
}

IPv4Address NetworkInterface::ipv4_address() const {
    return info().ip_addr;
}

IPv4Address NetworkInterface::ipv4_mask() const {
    return info().netmask;
}

IPv4Address NetworkInterface::ipv4_broadcast() const {
    return info().bcast_addr;
}

vector<NetworkInterface::IPv6Prefix> NetworkInterface::ipv6_addresses() const {
    return info().ipv6_addrs;
}

NetworkInterface::id_type NetworkInterface::resolve_index(const char* name) {
    #ifndef _WIN32
    id_type id = if_nametoindex(name);
    if (!id) {
        throw invalid_interface();
    }
    return id;
    #else // _WIN32
    ULONG size;
    ::GetAdaptersAddresses(AF_INET, 0, 0, 0, &size);
    vector<uint8_t> buffer(size);
    if (::GetAdaptersAddresses(AF_INET, 0, 0, (IP_ADAPTER_ADDRESSES *)&buffer[0], &size) == ERROR_SUCCESS) {
        PIP_ADAPTER_ADDRESSES iface = (IP_ADAPTER_ADDRESSES *)&buffer[0];
        while (iface) {
            if (strcmp(iface->AdapterName, name) == 0) {
                return iface->IfIndex;
            }
            iface = iface->Next;
        }
    }
    throw invalid_interface();
    #endif // _WIN32
}

} // Tins
<|endoftext|>


<commit_before>/**
* Copyright (C) 2014, BMW Car IT GmbH
* Author: Jonas Sticha (Jonas.Sticha@bmw-carit.de)
*
* This software is licensed under BSD 3-clause License
* (see http://spdx.org/licenses/BSD-3-Clause).
**/

#include "Subscriber.h"
#include <sstream>
#include <rt_tests_support/Logger.h>
#include <rt_tests_support/PlotDataFileCreator.h>

#define NANO_TO_MICRO_DIVISOR 1000

Subscriber::Subscriber(const std::string& topic) :
	config(Config::getConfig()),
	lastSeq(messageMissing), outOfOrderCounter(0), amountMessages(config->amountMessages),
	rosSubscriber(config->nodeHandle->subscribe(topic, 1000, &Subscriber::messageCallback, this)),
	measurementData(new MeasurementDataEvaluator(config->amountMessages))
{
}

void Subscriber::startMeasurement()
{
	lastSeq = messageMissing;
	outOfOrderCounter = 0;
	ros::spin();
	measurementData->analyzeData();
}

std::string Subscriber::getMeasurementSummary()
{
	std::stringstream ss;
	ss << "Amount messages: " << config->amountMessages << "; Messages out of order: " << getAmountMessagesOutOfOrder() << std::endl;
	ss << "MIN: " << measurementData->getMinValue() << "us\tAVG: " << measurementData->getAvgValue() << "us\tMAX: " << measurementData->getMaxValue() << "us" << std::endl;
	if(measurementData->getMinValue() == messageMissing)
	{
		int messagesMissing = 0;
		ss << "Missing messages: |";
		for(int i = 0; i < config->amountMessages; i++)
		{
			if(measurementData->getData()[i] == messageMissing)
			{
				ss << i << "|";
				messagesMissing++;
			}
		}
		ss << std::endl << "Total of " << messagesMissing << " missing messages.";
		Logger::INFO(ss.str());
	}
	return ss.str();
}

void Subscriber::saveGnuplotData()
{
	std::string filename = config->getFilename();
	std::stringstream measurementSummary(getMeasurementSummary());
	std::stringstream ss;
	ss << "set title \"" << config->getTitle() << "\"" << std::endl;
	ss << "set xlabel \"Latency in micro seconds - MIN:  ";
	ss << measurementData->getMinValue() << "us  AVG: " << measurementData->getAvgValue() << "us MAX: " << measurementData->getMaxValue() << "us\"" << std::endl;
	ss << "set ylabel \"Number of latency samples\"" << std::endl << "set yrange [0.7:]" << std::endl << "set logscale y" << std::endl;
	int xrange = measurementData->getMaxValue() + 50;
	ss << "set xrange [1:" << xrange << "]" << std::endl << "set xtics add(500, 1000)" << std::endl;
	ss << "set terminal jpeg size 1920,1080" << std::endl;
	ss << "set output \"" << filename << ".jpg\"" << std::endl;
	ss << "plot \"-\" u 1:2 t 'Latency_Occurrence' w steps" << std::endl;
	ss << "# Plot data for gnuplot" << std::endl;
	while(!measurementSummary.eof() && !measurementSummary.fail())
	{
		char line[512];
		measurementSummary.getline(line, 512);
		if(line[0] != '\0')
		{
			ss << "# " << line << std::endl;
		}
	}
	PlotDataFileCreator plotter;
	plotter.createPlottableDatafile(filename+".log", ss.str(), measurementData);
}

void Subscriber::printMeasurementResults()
{
	Logger::INFO("Measurement results:");
	std::stringstream measurementSummary(getMeasurementSummary());
	while(!measurementSummary.eof() && !measurementSummary.fail())
	{
		char line[512];
		measurementSummary.getline(line, 512);
		if(line[0] != '\0')
		{
			Logger::INFO(line);
		}
	}
}

MeasurementDataEvaluator* Subscriber::getMeasurementData()
{
	return measurementData;
}

int Subscriber::getAmountMessagesOutOfOrder()
{
	return outOfOrderCounter;
}

void Subscriber::messageCallback(const communication_tests::timestamp_msg::ConstPtr& msg)
{
	struct timespec ts;
	clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
	measurementData->getData()[msg->seq] = ((ts.tv_sec - msg->sec) * 1000000000 + (ts.tv_nsec - msg->nsec))/NANO_TO_MICRO_DIVISOR;
	if((int) msg->seq < lastSeq)
	{
		outOfOrderCounter++;
	}
	lastSeq = msg->seq;
	if(msg->last_msg)
	{
		ros::shutdown();
	}
}

Subscriber::~Subscriber()
{
	delete measurementData;
}
<commit_msg>communication_tests: Log index of max value<commit_after>/**
* Copyright (C) 2014, BMW Car IT GmbH
* Author: Jonas Sticha (Jonas.Sticha@bmw-carit.de)
*
* This software is licensed under BSD 3-clause License
* (see http://spdx.org/licenses/BSD-3-Clause).
**/

#include "Subscriber.h"
#include <sstream>
#include <rt_tests_support/Logger.h>
#include <rt_tests_support/PlotDataFileCreator.h>

#define NANO_TO_MICRO_DIVISOR 1000

Subscriber::Subscriber(const std::string& topic) :
	config(Config::getConfig()),
	lastSeq(messageMissing), outOfOrderCounter(0), amountMessages(config->amountMessages),
	rosSubscriber(config->nodeHandle->subscribe(topic, 1000, &Subscriber::messageCallback, this)),
	measurementData(new MeasurementDataEvaluator(config->amountMessages))
{
}

void Subscriber::startMeasurement()
{
	lastSeq = messageMissing;
	outOfOrderCounter = 0;
	ros::spin();
	measurementData->analyzeData();
}

std::string Subscriber::getMeasurementSummary()
{
	std::stringstream ss;
	ss << "Amount messages: " << config->amountMessages << "; Messages out of order: " << getAmountMessagesOutOfOrder();
	ss << "; MAX value index: " << measurementData->getMaxValueIndex() << std::endl;
	ss << "MIN: " << measurementData->getMinValue() << "us\tAVG: " << measurementData->getAvgValue() << "us\tMAX: " << measurementData->getMaxValue() << "us" << std::endl;
	if(measurementData->getMinValue() == messageMissing)
	{
		int messagesMissing = 0;
		ss << "Missing messages: |";
		for(int i = 0; i < config->amountMessages; i++)
		{
			if(measurementData->getData()[i] == messageMissing)
			{
				ss << i << "|";
				messagesMissing++;
			}
		}
		ss << std::endl << "Total of " << messagesMissing << " missing messages.";
		Logger::INFO(ss.str());
	}
	return ss.str();
}

void Subscriber::saveGnuplotData()
{
	std::string filename = config->getFilename();
	std::stringstream measurementSummary(getMeasurementSummary());
	std::stringstream ss;
	ss << "set title \"" << config->getTitle() << "\"" << std::endl;
	ss << "set xlabel \"Latency in micro seconds - MIN:  ";
	ss << measurementData->getMinValue() << "us  AVG: " << measurementData->getAvgValue() << "us MAX: " << measurementData->getMaxValue();
	ss << "us\tMAX value index: " << measurementData->getMaxValueIndex() << "\"" << std::endl;
	ss << "set ylabel \"Number of latency samples\"" << std::endl << "set yrange [0.7:]" << std::endl << "set logscale y" << std::endl;
	int xrange = measurementData->getMaxValue() + 50;
	ss << "set xrange [1:" << xrange << "]" << std::endl << "set xtics add(500, 1000)" << std::endl;
	ss << "set terminal jpeg size 1920,1080" << std::endl;
	ss << "set output \"" << filename << ".jpg\"" << std::endl;
	ss << "plot \"-\" u 1:2 t 'Latency_Occurrence' w steps" << std::endl;
	ss << "# Plot data for gnuplot" << std::endl;
	while(!measurementSummary.eof() && !measurementSummary.fail())
	{
		char line[512];
		measurementSummary.getline(line, 512);
		if(line[0] != '\0')
		{
			ss << "# " << line << std::endl;
		}
	}
	PlotDataFileCreator plotter;
	plotter.createPlottableDatafile(filename+".log", ss.str(), measurementData);
}

void Subscriber::printMeasurementResults()
{
	Logger::INFO("Measurement results:");
	std::stringstream measurementSummary(getMeasurementSummary());
	while(!measurementSummary.eof() && !measurementSummary.fail())
	{
		char line[512];
		measurementSummary.getline(line, 512);
		if(line[0] != '\0')
		{
			Logger::INFO(line);
		}
	}
}

MeasurementDataEvaluator* Subscriber::getMeasurementData()
{
	return measurementData;
}

int Subscriber::getAmountMessagesOutOfOrder()
{
	return outOfOrderCounter;
}

void Subscriber::messageCallback(const communication_tests::timestamp_msg::ConstPtr& msg)
{
	struct timespec ts;
	clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
	measurementData->getData()[msg->seq] = ((ts.tv_sec - msg->sec) * 1000000000 + (ts.tv_nsec - msg->nsec))/NANO_TO_MICRO_DIVISOR;
	if((int) msg->seq < lastSeq)
	{
		outOfOrderCounter++;
	}
	lastSeq = msg->seq;
	if(msg->last_msg)
	{
		ros::shutdown();
	}
}

Subscriber::~Subscriber()
{
	delete measurementData;
}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2014-2015 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <string>

#include "catch.hpp"

#include "cpp_utils/parallel.hpp"

TEST_CASE( "parallel_foreach/1", "[parallel]" ) {
    std::vector<int> ints{0, 1, 2, 3, 4, 5, 6};

    cpp::parallel_foreach(ints.begin(), ints.end(), [&ints](int a){
        ++ints[a];
    });

    REQUIRE(ints[0] == 1);
    REQUIRE(ints[1] == 2);
    REQUIRE(ints[2] == 3);
    REQUIRE(ints[3] == 4);
    REQUIRE(ints[4] == 5);
    REQUIRE(ints[5] == 6);
    REQUIRE(ints[6] == 7);
}

TEST_CASE( "parallel_foreach/2", "[parallel]" ) {
    std::vector<int> ints{0, 1, 2, 3, 4, 5, 6};

    cpp::parallel_foreach(ints, [&ints](int a){
        ++ints[a];
    });

    REQUIRE(ints[0] == 1);
    REQUIRE(ints[1] == 2);
    REQUIRE(ints[2] == 3);
    REQUIRE(ints[3] == 4);
    REQUIRE(ints[4] == 5);
    REQUIRE(ints[5] == 6);
    REQUIRE(ints[6] == 7);
}

TEST_CASE( "parallel_foreach_i/1", "[parallel]" ) {
    std::vector<int> ints{-1,2,0,3,4,1};

    cpp::parallel_foreach_i(ints.begin(), ints.end(), [&ints](int /*a*/, std::size_t i){
        ints[i] = i;
    });

    REQUIRE(ints[0] == 0);
    REQUIRE(ints[1] == 1);
    REQUIRE(ints[2] == 2);
    REQUIRE(ints[3] == 3);
    REQUIRE(ints[4] == 4);
    REQUIRE(ints[5] == 5);
}

TEST_CASE( "parallel_foreach_i/2", "[parallel]" ) {
    std::vector<int> ints{-1,2,0,3,4,1};

    cpp::parallel_foreach_i(ints, [&ints](int /*a*/, std::size_t i){
        ints[i] = i;
    });

    REQUIRE(ints[0] == 0);
    REQUIRE(ints[1] == 1);
    REQUIRE(ints[2] == 2);
    REQUIRE(ints[3] == 3);
    REQUIRE(ints[4] == 4); REQUIRE(ints[5] == 5);
}

TEST_CASE( "parallel_foreach_i_only/1", "[parallel]" ) {
    std::vector<int> ints{-1,2,0,3,4,1};

    cpp::parallel_foreach_i_only(ints.begin(), ints.end(), [&ints](std::size_t i){
        ints[i] = i;
    });

    REQUIRE(ints[0] == 0);
    REQUIRE(ints[1] == 1);
    REQUIRE(ints[2] == 2);
    REQUIRE(ints[3] == 3);
    REQUIRE(ints[4] == 4);
    REQUIRE(ints[5] == 5);
}

TEST_CASE( "parallel_foreach_i_only/2", "[parallel]" ) {
    std::vector<int> ints{-1,2,0,3,4,1};

    cpp::parallel_foreach_i_only(ints, [&ints](std::size_t i){
        ints[i] = i;
    });

    REQUIRE(ints[0] == 0);
    REQUIRE(ints[1] == 1);
    REQUIRE(ints[2] == 2);
    REQUIRE(ints[3] == 3);
    REQUIRE(ints[4] == 4);
    REQUIRE(ints[5] == 5);
}

TEST_CASE( "tp/parallel_foreach/1", "[parallel]" ) {
    std::vector<int> ints{0, 1, 2, 3, 4, 5, 6};

    cpp::default_thread_pool<> pool;
    cpp::parallel_foreach(pool, ints.begin(), ints.end(), [&ints](int a){
        ++ints[a];
    });

    REQUIRE(ints[0] == 1);
    REQUIRE(ints[1] == 2);
    REQUIRE(ints[2] == 3);
    REQUIRE(ints[3] == 4);
    REQUIRE(ints[4] == 5);
    REQUIRE(ints[5] == 6);
    REQUIRE(ints[6] == 7);
}

TEST_CASE( "tp/parallel_foreach/2", "[parallel]" ) {
    std::vector<int> ints{0, 1, 2, 3, 4, 5, 6};

    cpp::default_thread_pool<> pool;
    cpp::parallel_foreach(pool, ints, [&ints](int a){
        ++ints[a];
    });

    REQUIRE(ints[0] == 1);
    REQUIRE(ints[1] == 2);
    REQUIRE(ints[2] == 3);
    REQUIRE(ints[3] == 4);
    REQUIRE(ints[4] == 5);
    REQUIRE(ints[5] == 6);
    REQUIRE(ints[6] == 7);
}

TEST_CASE( "tp/parallel_foreach_i/1", "[parallel]" ) {
    std::vector<int> ints{-1,2,0,3,4,1};

    cpp::default_thread_pool<> pool;
    cpp::parallel_foreach_i(pool, ints.begin(), ints.end(), [&ints](int /*a*/, std::size_t i){
        ints[i] = i;
    });

    REQUIRE(ints[0] == 0);
    REQUIRE(ints[1] == 1);
    REQUIRE(ints[2] == 2);
    REQUIRE(ints[3] == 3);
    REQUIRE(ints[4] == 4);
    REQUIRE(ints[5] == 5);
}

TEST_CASE( "tp/parallel_foreach_i/2", "[parallel]" ) {
    std::vector<int> ints{-1,2,0,3,4,1};

    cpp::default_thread_pool<> pool;
    cpp::parallel_foreach_i(pool, ints, [&ints](int /*a*/, std::size_t i){
        ints[i] = i;
    });

    REQUIRE(ints[0] == 0);
    REQUIRE(ints[1] == 1);
    REQUIRE(ints[2] == 2);
    REQUIRE(ints[3] == 3);
    REQUIRE(ints[4] == 4); REQUIRE(ints[5] == 5);
}

TEST_CASE( "tp/parallel_foreach_i_only/1", "[parallel]" ) {
    std::vector<int> ints{-1,2,0,3,4,1};

    cpp::default_thread_pool<> pool;
    cpp::parallel_foreach_i_only(pool, ints.begin(), ints.end(), [&ints](std::size_t i){
        ints[i] = i;
    });

    REQUIRE(ints[0] == 0);
    REQUIRE(ints[1] == 1);
    REQUIRE(ints[2] == 2);
    REQUIRE(ints[3] == 3);
    REQUIRE(ints[4] == 4);
    REQUIRE(ints[5] == 5);
}

TEST_CASE( "tp/parallel_foreach_i_only/2", "[parallel]" ) {
    std::vector<int> ints{-1,2,0,3,4,1};

    cpp::default_thread_pool<> pool;
    cpp::parallel_foreach_i_only(pool, ints, [&ints](std::size_t i){
        ints[i] = i;
    });

    REQUIRE(ints[0] == 0);
    REQUIRE(ints[1] == 1);
    REQUIRE(ints[2] == 2);
    REQUIRE(ints[3] == 3);
    REQUIRE(ints[4] == 4);
    REQUIRE(ints[5] == 5);
}
<commit_msg>Tests for parallel foreach_n<commit_after>//=======================================================================
// Copyright (c) 2014-2015 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <string>

#include "catch.hpp"

#include "cpp_utils/parallel.hpp"

TEST_CASE( "parallel_foreach_n/1", "[parallel]" ) {
    std::vector<int> ints{0, 1, 2, 3, 4, 5, 6};

    cpp::parallel_foreach_n(0, 7, [&ints](std::size_t a){
        ++ints[a];
    });

    REQUIRE(ints[0] == 1);
    REQUIRE(ints[1] == 2);
    REQUIRE(ints[2] == 3);
    REQUIRE(ints[3] == 4);
    REQUIRE(ints[4] == 5);
    REQUIRE(ints[5] == 6);
    REQUIRE(ints[6] == 7);
}

TEST_CASE( "parallel_foreach/1", "[parallel]" ) {
    std::vector<int> ints{0, 1, 2, 3, 4, 5, 6};

    cpp::parallel_foreach(ints.begin(), ints.end(), [&ints](int a){
        ++ints[a];
    });

    REQUIRE(ints[0] == 1);
    REQUIRE(ints[1] == 2);
    REQUIRE(ints[2] == 3);
    REQUIRE(ints[3] == 4);
    REQUIRE(ints[4] == 5);
    REQUIRE(ints[5] == 6);
    REQUIRE(ints[6] == 7);
}

TEST_CASE( "parallel_foreach/2", "[parallel]" ) {
    std::vector<int> ints{0, 1, 2, 3, 4, 5, 6};

    cpp::parallel_foreach(ints, [&ints](int a){
        ++ints[a];
    });

    REQUIRE(ints[0] == 1);
    REQUIRE(ints[1] == 2);
    REQUIRE(ints[2] == 3);
    REQUIRE(ints[3] == 4);
    REQUIRE(ints[4] == 5);
    REQUIRE(ints[5] == 6);
    REQUIRE(ints[6] == 7);
}

TEST_CASE( "parallel_foreach_i/1", "[parallel]" ) {
    std::vector<int> ints{-1,2,0,3,4,1};

    cpp::parallel_foreach_i(ints.begin(), ints.end(), [&ints](int /*a*/, std::size_t i){
        ints[i] = i;
    });

    REQUIRE(ints[0] == 0);
    REQUIRE(ints[1] == 1);
    REQUIRE(ints[2] == 2);
    REQUIRE(ints[3] == 3);
    REQUIRE(ints[4] == 4);
    REQUIRE(ints[5] == 5);
}

TEST_CASE( "parallel_foreach_i/2", "[parallel]" ) {
    std::vector<int> ints{-1,2,0,3,4,1};

    cpp::parallel_foreach_i(ints, [&ints](int /*a*/, std::size_t i){
        ints[i] = i;
    });

    REQUIRE(ints[0] == 0);
    REQUIRE(ints[1] == 1);
    REQUIRE(ints[2] == 2);
    REQUIRE(ints[3] == 3);
    REQUIRE(ints[4] == 4); REQUIRE(ints[5] == 5);
}

TEST_CASE( "parallel_foreach_i_only/1", "[parallel]" ) {
    std::vector<int> ints{-1,2,0,3,4,1};

    cpp::parallel_foreach_i_only(ints.begin(), ints.end(), [&ints](std::size_t i){
        ints[i] = i;
    });

    REQUIRE(ints[0] == 0);
    REQUIRE(ints[1] == 1);
    REQUIRE(ints[2] == 2);
    REQUIRE(ints[3] == 3);
    REQUIRE(ints[4] == 4);
    REQUIRE(ints[5] == 5);
}

TEST_CASE( "parallel_foreach_i_only/2", "[parallel]" ) {
    std::vector<int> ints{-1,2,0,3,4,1};

    cpp::parallel_foreach_i_only(ints, [&ints](std::size_t i){
        ints[i] = i;
    });

    REQUIRE(ints[0] == 0);
    REQUIRE(ints[1] == 1);
    REQUIRE(ints[2] == 2);
    REQUIRE(ints[3] == 3);
    REQUIRE(ints[4] == 4);
    REQUIRE(ints[5] == 5);
}

TEST_CASE( "tp/parallel_foreach_n/1", "[parallel]" ) {
    std::vector<int> ints{0, 1, 2, 3, 4, 5, 6};

    cpp::default_thread_pool<> pool;
    cpp::parallel_foreach_n(pool, 0, 7, [&ints](int a){
        ++ints[a];
    });

    REQUIRE(ints[0] == 1);
    REQUIRE(ints[1] == 2);
    REQUIRE(ints[2] == 3);
    REQUIRE(ints[3] == 4);
    REQUIRE(ints[4] == 5);
    REQUIRE(ints[5] == 6);
    REQUIRE(ints[6] == 7);
}

TEST_CASE( "tp/parallel_foreach/1", "[parallel]" ) {
    std::vector<int> ints{0, 1, 2, 3, 4, 5, 6};

    cpp::default_thread_pool<> pool;
    cpp::parallel_foreach(pool, ints.begin(), ints.end(), [&ints](int a){
        ++ints[a];
    });

    REQUIRE(ints[0] == 1);
    REQUIRE(ints[1] == 2);
    REQUIRE(ints[2] == 3);
    REQUIRE(ints[3] == 4);
    REQUIRE(ints[4] == 5);
    REQUIRE(ints[5] == 6);
    REQUIRE(ints[6] == 7);
}

TEST_CASE( "tp/parallel_foreach/2", "[parallel]" ) {
    std::vector<int> ints{0, 1, 2, 3, 4, 5, 6};

    cpp::default_thread_pool<> pool;
    cpp::parallel_foreach(pool, ints, [&ints](int a){
        ++ints[a];
    });

    REQUIRE(ints[0] == 1);
    REQUIRE(ints[1] == 2);
    REQUIRE(ints[2] == 3);
    REQUIRE(ints[3] == 4);
    REQUIRE(ints[4] == 5);
    REQUIRE(ints[5] == 6);
    REQUIRE(ints[6] == 7);
}

TEST_CASE( "tp/parallel_foreach_i/1", "[parallel]" ) {
    std::vector<int> ints{-1,2,0,3,4,1};

    cpp::default_thread_pool<> pool;
    cpp::parallel_foreach_i(pool, ints.begin(), ints.end(), [&ints](int /*a*/, std::size_t i){
        ints[i] = i;
    });

    REQUIRE(ints[0] == 0);
    REQUIRE(ints[1] == 1);
    REQUIRE(ints[2] == 2);
    REQUIRE(ints[3] == 3);
    REQUIRE(ints[4] == 4);
    REQUIRE(ints[5] == 5);
}

TEST_CASE( "tp/parallel_foreach_i/2", "[parallel]" ) {
    std::vector<int> ints{-1,2,0,3,4,1};

    cpp::default_thread_pool<> pool;
    cpp::parallel_foreach_i(pool, ints, [&ints](int /*a*/, std::size_t i){
        ints[i] = i;
    });

    REQUIRE(ints[0] == 0);
    REQUIRE(ints[1] == 1);
    REQUIRE(ints[2] == 2);
    REQUIRE(ints[3] == 3);
    REQUIRE(ints[4] == 4); REQUIRE(ints[5] == 5);
}

TEST_CASE( "tp/parallel_foreach_i_only/1", "[parallel]" ) {
    std::vector<int> ints{-1,2,0,3,4,1};

    cpp::default_thread_pool<> pool;
    cpp::parallel_foreach_i_only(pool, ints.begin(), ints.end(), [&ints](std::size_t i){
        ints[i] = i;
    });

    REQUIRE(ints[0] == 0);
    REQUIRE(ints[1] == 1);
    REQUIRE(ints[2] == 2);
    REQUIRE(ints[3] == 3);
    REQUIRE(ints[4] == 4);
    REQUIRE(ints[5] == 5);
}

TEST_CASE( "tp/parallel_foreach_i_only/2", "[parallel]" ) {
    std::vector<int> ints{-1,2,0,3,4,1};

    cpp::default_thread_pool<> pool;
    cpp::parallel_foreach_i_only(pool, ints, [&ints](std::size_t i){
        ints[i] = i;
    });

    REQUIRE(ints[0] == 0);
    REQUIRE(ints[1] == 1);
    REQUIRE(ints[2] == 2);
    REQUIRE(ints[3] == 3);
    REQUIRE(ints[4] == 4);
    REQUIRE(ints[5] == 5);
}
<|endoftext|>


<commit_before>#ifndef ENGINE_GUIDANCE_ASSEMBLE_OVERVIEW_HPP
#define ENGINE_GUIDANCE_ASSEMBLE_OVERVIEW_HPP

#include "engine/douglas_peucker.hpp"
#include "engine/guidance/leg_geometry.hpp"
#include "util/viewport.hpp"

#include <iterator>
#include <limits>
#include <numeric>
#include <tuple>
#include <utility>
#include <vector>

namespace osrm
{
namespace engine
{
namespace guidance
{
namespace
{

unsigned calculateOverviewZoomLevel(const std::vector<LegGeometry> &leg_geometries)
{
    util::Coordinate south_west{util::FixedLongitude{std::numeric_limits<int>::max()},
                                util::FixedLatitude{std::numeric_limits<int>::max()}};
    util::Coordinate north_east{util::FixedLongitude{std::numeric_limits<int>::min()},
                                util::FixedLatitude{std::numeric_limits<int>::min()}};

    for (const auto &leg_geometry : leg_geometries)
    {
        for (const auto coord : leg_geometry.locations)
        {
            south_west.lon = std::min(south_west.lon, coord.lon);
            south_west.lat = std::min(south_west.lat, coord.lat);

            north_east.lon = std::max(north_east.lon, coord.lon);
            north_east.lat = std::max(north_east.lat, coord.lat);
        }
    }

    return util::viewport::getFittedZoom(south_west, north_east);
}

std::vector<util::Coordinate> simplifyGeometry(const std::vector<LegGeometry> &leg_geometries,
                                               const unsigned zoom_level)
{
    std::vector<util::Coordinate> overview_geometry;
    auto leg_index = 0UL;
    for (const auto &geometry : leg_geometries)
    {
        auto simplified_geometry =
            douglasPeucker(geometry.locations.begin(), geometry.locations.end(), zoom_level);
        // not the last leg
        if (leg_index < leg_geometries.size() - 1)
        {
            simplified_geometry.pop_back();
        }
        overview_geometry.insert(
            overview_geometry.end(), simplified_geometry.begin(), simplified_geometry.end());
    }
    return overview_geometry;
}
}

std::vector<util::Coordinate> assembleOverview(const std::vector<LegGeometry> &leg_geometries,
                                               const bool use_simplification)
{
    if (use_simplification)
    {
        const auto zoom_level = std::min(18u, calculateOverviewZoomLevel(leg_geometries));
        return simplifyGeometry(leg_geometries, zoom_level);
    }
    BOOST_ASSERT(!use_simplification);

    auto overview_size =
        std::accumulate(leg_geometries.begin(),
                        leg_geometries.end(),
                        0,
                        [](const std::size_t sum, const LegGeometry &leg_geometry) {
                            return sum + leg_geometry.locations.size();
                        }) -
        leg_geometries.size() + 1;
    std::vector<util::Coordinate> overview_geometry;
    overview_geometry.reserve(overview_size);

    auto leg_reverse_index = leg_geometries.size();
    for (const auto &geometry : leg_geometries)
    {
        auto begin = geometry.locations.begin();
        auto end = geometry.locations.end();
        if (--leg_reverse_index > 0)
        {
            end = std::prev(end);
        }
        overview_geometry.insert(overview_geometry.end(), begin, end);
    }

    return overview_geometry;
}

} // namespace guidance
} // namespace engine
} // namespace osrm

#endif
<commit_msg>Fix non-overlap logic for simplified geometries.<commit_after>#ifndef ENGINE_GUIDANCE_ASSEMBLE_OVERVIEW_HPP
#define ENGINE_GUIDANCE_ASSEMBLE_OVERVIEW_HPP

#include "engine/douglas_peucker.hpp"
#include "engine/guidance/leg_geometry.hpp"
#include "util/viewport.hpp"

#include <iterator>
#include <limits>
#include <numeric>
#include <tuple>
#include <utility>
#include <vector>

namespace osrm
{
namespace engine
{
namespace guidance
{
namespace
{

unsigned calculateOverviewZoomLevel(const std::vector<LegGeometry> &leg_geometries)
{
    util::Coordinate south_west{util::FixedLongitude{std::numeric_limits<int>::max()},
                                util::FixedLatitude{std::numeric_limits<int>::max()}};
    util::Coordinate north_east{util::FixedLongitude{std::numeric_limits<int>::min()},
                                util::FixedLatitude{std::numeric_limits<int>::min()}};

    for (const auto &leg_geometry : leg_geometries)
    {
        for (const auto coord : leg_geometry.locations)
        {
            south_west.lon = std::min(south_west.lon, coord.lon);
            south_west.lat = std::min(south_west.lat, coord.lat);

            north_east.lon = std::max(north_east.lon, coord.lon);
            north_east.lat = std::max(north_east.lat, coord.lat);
        }
    }

    return util::viewport::getFittedZoom(south_west, north_east);
}

}

std::vector<util::Coordinate> assembleOverview(const std::vector<LegGeometry> &leg_geometries,
                                               const bool use_simplification)
{
    auto overview_size =
        std::accumulate(leg_geometries.begin(),
                        leg_geometries.end(),
                        0,
                        [](const std::size_t sum, const LegGeometry &leg_geometry) {
                            return sum + leg_geometry.locations.size();
                        }) -
        leg_geometries.size() + 1;
    std::vector<util::Coordinate> overview_geometry;
    overview_geometry.reserve(overview_size);

    using GeometryIter = decltype(overview_geometry)::const_iterator;

    auto leg_reverse_index = leg_geometries.size();
    const auto insert_without_overlap = [&leg_reverse_index, &overview_geometry](GeometryIter begin, GeometryIter end) {
        // not the last leg
        if (--leg_reverse_index > 0)
        {
            end = std::prev(end);
        }
        overview_geometry.insert(overview_geometry.end(), begin, end);
    };

    if (use_simplification)
    {
        const auto zoom_level = std::min(18u, calculateOverviewZoomLevel(leg_geometries));
        for (const auto &geometry : leg_geometries)
        {
            auto simplified = douglasPeucker(geometry.locations.begin(), geometry.locations.end(), zoom_level);
            auto begin = simplified.cbegin();
            auto end = simplified.cend();
            insert_without_overlap(begin, end);
        }
    }
    else
    {
        for (const auto &geometry : leg_geometries)
        {
            auto begin = geometry.locations.begin();
            auto end = geometry.locations.end();
            insert_without_overlap(begin, end);
        }
    }

    return overview_geometry;
}

} // namespace guidance
} // namespace engine
} // namespace osrm

#endif
<|endoftext|>


<commit_before>/* -*-  mode: c++; c-default-style: "google"; indent-tabs-mode: nil -*- */
/* -------------------------------------------------------------------------
ATS

License: see $ATS_DIR/COPYRIGHT
Author: Ethan Coon

Interface for the derived StrongMPC class.  Is both a PK and a Model
Evalulator, providing needed methods for BDF time integration of the coupled
system.

Completely automated and generic to any sub PKs, this uses a block diagonal
preconditioner.

See additional documentation in the base class src/pks/mpc/MPC.hh
------------------------------------------------------------------------- */

#ifndef PKS_MPC_STRONG_MPC_HH_
#define PKS_MPC_STRONG_MPC_HH_

#include <vector>

#include "mpc.hh"
#include "pk_bdf_base.hh"

namespace Amanzi {

// note this looks odd, but StrongMPC is both a MPC within a hierarchy of BDF
// PKs, but it also IS a BDF PK itself, in that it implements the BDF
// interface.
template <class PK_t>
class StrongMPC : public MPC<PK_t>,
                  public PKBDFBase {

public:
  StrongMPC(const Teuchos::RCP<Teuchos::ParameterList>& plist,
            Teuchos::ParameterList& FElist,
            const Teuchos::RCP<TreeVector>& soln);

  // Virtual destructor
  virtual ~StrongMPC() {}

  virtual void setup(const Teuchos::Ptr<State>& S);
  virtual void initialize(const Teuchos::Ptr<State>& S);

  // StrongMPC is a BDFFnBase
  // -- computes the non-linear functional g = g(t,u,udot)
  //    By default this just calls each sub pk Functional().
  virtual void Functional(double t_old, double t_new, Teuchos::RCP<TreeVector> u_old,
           Teuchos::RCP<TreeVector> u_new, Teuchos::RCP<TreeVector> g);

  // -- enorm for the coupled system
  virtual double ErrorNorm(Teuchos::RCP<const TreeVector> u,
                       Teuchos::RCP<const TreeVector> du);

  // StrongMPC's preconditioner is, by default, just the block-diagonal
  // operator formed by placing the sub PK's preconditioners on the diagonal.
  // -- Apply preconditioner to u and returns the result in Pu.
  virtual void ApplyPreconditioner(Teuchos::RCP<const TreeVector> u, Teuchos::RCP<TreeVector> Pu);

  // -- Update the preconditioner.
  virtual void UpdatePreconditioner(double t, Teuchos::RCP<const TreeVector> up, double h);

  // -- Experimental approach -- calling this indicates that the time
  //    integration scheme is changing the value of the solution in
  //    state.
  virtual void ChangedSolution();

  // -- Admissibility of the solution.
  virtual bool IsAdmissible(Teuchos::RCP<const TreeVector> u);

  // -- Modify the predictor.
  virtual bool ModifyPredictor(double h, Teuchos::RCP<const TreeVector> u0,
          Teuchos::RCP<TreeVector> u);

  // -- Modify the correction.
  virtual ModifyCorrectionResult ModifyCorrection(double h, Teuchos::RCP<const TreeVector> res,
          Teuchos::RCP<const TreeVector> u, Teuchos::RCP<TreeVector> du);

protected:
  using MPC<PK_t>::sub_pks_;

private:
  // factory registration
  static RegisteredPKFactory<StrongMPC> reg_;

};

// -----------------------------------------------------------------------------
// Constructor
// -----------------------------------------------------------------------------
template<class PK_t>
StrongMPC<PK_t>::StrongMPC(const Teuchos::RCP<Teuchos::ParameterList>& plist,
                           Teuchos::ParameterList& FElist,
                           const Teuchos::RCP<TreeVector>& soln) :
    PKDefaultBase(plist, FElist, soln),
    MPC<PK_t>(plist, FElist, soln),
    PKBDFBase(plist, FElist, soln) {}

// -----------------------------------------------------------------------------
// Setup
// -----------------------------------------------------------------------------
template<class PK_t>
void StrongMPC<PK_t>::setup(const Teuchos::Ptr<State>& S) {
  // tweak the sub-PK parameter lists
  Teuchos::RCP<Teuchos::ParameterList> pks_list = Teuchos::sublist(plist_, "PKs");
  for (Teuchos::ParameterList::ConstIterator param=pks_list->begin();
       param!=pks_list->end(); ++param) {
    std::string pname = param->first;
    if (pks_list->isSublist(pname)) {
      pks_list->sublist(pname).set("strongly coupled PK", true);
    } else {
      ASSERT(0);
    }
  }

  MPC<PK_t>::setup(S);
  PKBDFBase::setup(S);

  // Set the initial timestep as the min of the sub-pk sizes.
  dt_ = 1.0e99;
  for (typename MPC<PK_t>::SubPKList::iterator pk = MPC<PK_t>::sub_pks_.begin();
       pk != MPC<PK_t>::sub_pks_.end(); ++pk) {
    dt_ = std::min<double>(dt_, (*pk)->get_dt());
  }

};

// -----------------------------------------------------------------------------
// Required unique initialize(), just calls both of its base class
// initialize() methods.
// -----------------------------------------------------------------------------
template<class PK_t>
void StrongMPC<PK_t>::initialize(const Teuchos::Ptr<State>& S) {
  // Just calls both subclass's initialize.  NOTE - order is important here --
  // MPC<PK_t> grabs the primary variables from each sub-PK and stuffs
  // them into the solution, which must be done prior to BDFBase initializing
  // the timestepper.

  // Initialize all sub PKs.
  MPC<PK_t>::initialize(S);

  // Initialize my timestepper.
  PKBDFBase::initialize(S);
};

// -----------------------------------------------------------------------------
// Compute the non-linear functional g = g(t,u,udot).
// -----------------------------------------------------------------------------
template<class PK_t>
void StrongMPC<PK_t>::Functional(double t_old, double t_new, Teuchos::RCP<TreeVector> u_old,
                    Teuchos::RCP<TreeVector> u_new, Teuchos::RCP<TreeVector> g) {
  solution_to_state(u_new, S_next_);

  // loop over sub-PKs
  for (unsigned int i=0; i!=sub_pks_.size(); ++i) {
    // pull out the old solution sub-vector
    Teuchos::RCP<TreeVector> pk_u_old(Teuchos::null);
    if (u_old != Teuchos::null) {
      pk_u_old = u_old->SubVector(i);
      if (pk_u_old == Teuchos::null) {
        Errors::Message message("MPC: vector structure does not match PK structure");
        Exceptions::amanzi_throw(message);
      }
    }

    // pull out the new solution sub-vector
    Teuchos::RCP<TreeVector> pk_u_new = u_new->SubVector(i);
    if (pk_u_new == Teuchos::null) {
      Errors::Message message("MPC: vector structure does not match PK structure");
      Exceptions::amanzi_throw(message);
    }

    // pull out the residual sub-vector
    Teuchos::RCP<TreeVector> pk_g = g->SubVector(i);
    if (pk_g == Teuchos::null) {
      Errors::Message message("MPC: vector structure does not match PK structure");
      Exceptions::amanzi_throw(message);
    }

    // fill the nonlinear function with each sub-PKs contribution
    sub_pks_[i]->Functional(t_old, t_new, pk_u_old, pk_u_new, pk_g);
  }
};

// -----------------------------------------------------------------------------
// Applies preconditioner to u and returns the result in Pu.
// -----------------------------------------------------------------------------
template<class PK_t>
void StrongMPC<PK_t>::ApplyPreconditioner(Teuchos::RCP<const TreeVector> u, Teuchos::RCP<TreeVector> Pu) {
  // loop over sub-PKs
  for (unsigned int i=0; i!=sub_pks_.size(); ++i) {
    // pull out the u sub-vector
    Teuchos::RCP<const TreeVector> pk_u = u->SubVector(i);
    if (pk_u == Teuchos::null) {
      Errors::Message message("MPC: vector structure does not match PK structure");
      Exceptions::amanzi_throw(message);
    }

    // pull out the preconditioned u sub-vector
    Teuchos::RCP<TreeVector> pk_Pu = Pu->SubVector(i);
    if (pk_Pu == Teuchos::null) {
      Errors::Message message("MPC: vector structure does not match PK structure");
      Exceptions::amanzi_throw(message);
    }

    // Fill the preconditioned u as the block-diagonal product using each sub-PK.
    sub_pks_[i]->ApplyPreconditioner(pk_u, pk_Pu);
  }

//   std::cout<<*(((Pu->SubVector("flow"))->Data())->ViewComponent("cell", false));
//   cout<<"Exit from StrongMPC precon\n";
//   exit(0);
};

// -----------------------------------------------------------------------------
// Compute a norm on u-du and returns the result.
// For a Strong MPC, the enorm is just the max of the sub PKs enorms.
// -----------------------------------------------------------------------------
template<class PK_t>
double StrongMPC<PK_t>::ErrorNorm(Teuchos::RCP<const TreeVector> u,
                        Teuchos::RCP<const TreeVector> du){
  double norm = 0.0;

  // loop over sub-PKs
  for (unsigned int i=0; i!=sub_pks_.size(); ++i) {
    // pull out the u sub-vector
    Teuchos::RCP<const TreeVector> pk_u = u->SubVector(i);
    if (pk_u == Teuchos::null) {
      Errors::Message message("MPC: vector structure does not match PK structure");
      Exceptions::amanzi_throw(message);
    }

    // pull out the du sub-vector
    Teuchos::RCP<const TreeVector> pk_du = du->SubVector(i);
    if (pk_du == Teuchos::null) {
      Errors::Message message("MPC: vector structure does not match PK structure");
      Exceptions::amanzi_throw(message);
    }

    // norm is the max of the sub-PK norms
    double tmp_norm = sub_pks_[i]->ErrorNorm(pk_u, pk_du);
    norm = std::max(norm, tmp_norm);
  }
  return norm;
};

// -----------------------------------------------------------------------------
// Update the preconditioner.
// -----------------------------------------------------------------------------
template<class PK_t>
void StrongMPC<PK_t>::UpdatePreconditioner(double t, Teuchos::RCP<const TreeVector> up, double h) {
  PKDefaultBase::solution_to_state(up, S_next_);

  // loop over sub-PKs
  for (unsigned int i=0; i!=sub_pks_.size(); ++i) {
    // pull out the up sub-vector
    Teuchos::RCP<const TreeVector> pk_up = up->SubVector(i);
    if (pk_up == Teuchos::null) {
      Errors::Message message("MPC: vector structure does not match PK structure");
      Exceptions::amanzi_throw(message);
    }

    // update precons of each of the sub-PKs
    sub_pks_[i]->UpdatePreconditioner(t, pk_up, h);
  };
};

// -----------------------------------------------------------------------------
// Experimental approach -- calling this indicates that the time integration
// scheme is changing the value of the solution in state.
// -----------------------------------------------------------------------------
template<class PK_t>
void StrongMPC<PK_t>::ChangedSolution() {
  // loop over sub-PKs
  for (typename MPC<PK_t>::SubPKList::iterator pk = MPC<PK_t>::sub_pks_.begin();
       pk != MPC<PK_t>::sub_pks_.end(); ++pk) {
    (*pk)->ChangedSolution();
  }
};

// -----------------------------------------------------------------------------
// Check admissibility of each sub-pk
// -----------------------------------------------------------------------------
template<class PK_t>
bool StrongMPC<PK_t>::IsAdmissible(Teuchos::RCP<const TreeVector> u) {
  // First ensure each PK thinks we are admissible -- this will ensure
  // the residual can at least be evaluated.
  for (unsigned int i=0; i!=sub_pks_.size(); ++i) {
    // pull out the u sub-vector
    Teuchos::RCP<const TreeVector> pk_u = u->SubVector(i);
    if (pk_u == Teuchos::null) {
      Errors::Message message("MPC: vector structure does not match PK structure");
      Exceptions::amanzi_throw(message);
    }

    if (!sub_pks_[i]->IsAdmissible(pk_u)) {
      if (vo_->os_OK(Teuchos::VERB_HIGH))
        *vo_->os() << "PK " << sub_pks_[i]->name() << " is not admissible." << std::endl;

      return false;
    }
  }

  // If that worked, check backtracking admissility.
  return PKBDFBase::IsAdmissible(u);
};

// -----------------------------------------------------------------------------
// Modify predictor from each sub pk.
// -----------------------------------------------------------------------------
template<class PK_t>
bool StrongMPC<PK_t>::ModifyPredictor(double h, Teuchos::RCP<const TreeVector> u0,
        Teuchos::RCP<TreeVector> u) {
  // loop over sub-PKs
  bool modified = false;
  for (unsigned int i=0; i!=sub_pks_.size(); ++i) {
    // pull out the u sub-vector
    Teuchos::RCP<const TreeVector> pk_u0 = u0->SubVector(i);
    Teuchos::RCP<TreeVector> pk_u = u->SubVector(i);
    if (pk_u == Teuchos::null) {
      Errors::Message message("MPC: vector structure does not match PK structure");
      Exceptions::amanzi_throw(message);
    }

    modified |= sub_pks_[i]->ModifyPredictor(h, pk_u0, pk_u);
  }
  return modified;
};

// -----------------------------------------------------------------------------
// Modify correction from each sub pk.
// -----------------------------------------------------------------------------
template<class PK_t>
ModifyCorrectionResult StrongMPC<PK_t>::ModifyCorrection(double h,
        Teuchos::RCP<const TreeVector> res,
        Teuchos::RCP<const TreeVector> u, Teuchos::RCP<TreeVector> du) {
  // loop over sub-PKs
  bool modified = false;
  for (unsigned int i=0; i!=sub_pks_.size(); ++i) {
    // pull out the u sub-vector
    Teuchos::RCP<const TreeVector> pk_u = u->SubVector(i);
    Teuchos::RCP<const TreeVector> pk_res = res->SubVector(i);
    Teuchos::RCP<TreeVector> pk_du = du->SubVector(i);

    if (pk_u == Teuchos::null || pk_du == Teuchos::null) {
      Errors::Message message("MPC: vector structure does not match PK structure");
      Exceptions::amanzi_throw(message);
    }

    modified |= sub_pks_[i]->ModifyCorrection(h, pk_res, pk_u, pk_du);
  }
  return modified ? AmanziSolvers::CORRECTION_MODIFIED :
      AmanziSolvers::CORRECTION_NOT_MODIFIED;
};

} // close namespace Amanzi

#endif
<commit_msg>bug fix to let strong mpc forward max return code for ModifyCorrection<commit_after>/* -*-  mode: c++; c-default-style: "google"; indent-tabs-mode: nil -*- */
/* -------------------------------------------------------------------------
ATS

License: see $ATS_DIR/COPYRIGHT
Author: Ethan Coon

Interface for the derived StrongMPC class.  Is both a PK and a Model
Evalulator, providing needed methods for BDF time integration of the coupled
system.

Completely automated and generic to any sub PKs, this uses a block diagonal
preconditioner.

See additional documentation in the base class src/pks/mpc/MPC.hh
------------------------------------------------------------------------- */

#ifndef PKS_MPC_STRONG_MPC_HH_
#define PKS_MPC_STRONG_MPC_HH_

#include <vector>

#include "mpc.hh"
#include "pk_bdf_base.hh"

namespace Amanzi {

// note this looks odd, but StrongMPC is both a MPC within a hierarchy of BDF
// PKs, but it also IS a BDF PK itself, in that it implements the BDF
// interface.
template <class PK_t>
class StrongMPC : public MPC<PK_t>,
                  public PKBDFBase {

public:
  StrongMPC(const Teuchos::RCP<Teuchos::ParameterList>& plist,
            Teuchos::ParameterList& FElist,
            const Teuchos::RCP<TreeVector>& soln);

  // Virtual destructor
  virtual ~StrongMPC() {}

  virtual void setup(const Teuchos::Ptr<State>& S);
  virtual void initialize(const Teuchos::Ptr<State>& S);

  // StrongMPC is a BDFFnBase
  // -- computes the non-linear functional g = g(t,u,udot)
  //    By default this just calls each sub pk Functional().
  virtual void Functional(double t_old, double t_new, Teuchos::RCP<TreeVector> u_old,
           Teuchos::RCP<TreeVector> u_new, Teuchos::RCP<TreeVector> g);

  // -- enorm for the coupled system
  virtual double ErrorNorm(Teuchos::RCP<const TreeVector> u,
                       Teuchos::RCP<const TreeVector> du);

  // StrongMPC's preconditioner is, by default, just the block-diagonal
  // operator formed by placing the sub PK's preconditioners on the diagonal.
  // -- Apply preconditioner to u and returns the result in Pu.
  virtual void ApplyPreconditioner(Teuchos::RCP<const TreeVector> u, Teuchos::RCP<TreeVector> Pu);

  // -- Update the preconditioner.
  virtual void UpdatePreconditioner(double t, Teuchos::RCP<const TreeVector> up, double h);

  // -- Experimental approach -- calling this indicates that the time
  //    integration scheme is changing the value of the solution in
  //    state.
  virtual void ChangedSolution();

  // -- Admissibility of the solution.
  virtual bool IsAdmissible(Teuchos::RCP<const TreeVector> u);

  // -- Modify the predictor.
  virtual bool ModifyPredictor(double h, Teuchos::RCP<const TreeVector> u0,
          Teuchos::RCP<TreeVector> u);

  // -- Modify the correction.
  virtual ModifyCorrectionResult ModifyCorrection(double h, Teuchos::RCP<const TreeVector> res,
          Teuchos::RCP<const TreeVector> u, Teuchos::RCP<TreeVector> du);

protected:
  using MPC<PK_t>::sub_pks_;

private:
  // factory registration
  static RegisteredPKFactory<StrongMPC> reg_;

};

// -----------------------------------------------------------------------------
// Constructor
// -----------------------------------------------------------------------------
template<class PK_t>
StrongMPC<PK_t>::StrongMPC(const Teuchos::RCP<Teuchos::ParameterList>& plist,
                           Teuchos::ParameterList& FElist,
                           const Teuchos::RCP<TreeVector>& soln) :
    PKDefaultBase(plist, FElist, soln),
    MPC<PK_t>(plist, FElist, soln),
    PKBDFBase(plist, FElist, soln) {}

// -----------------------------------------------------------------------------
// Setup
// -----------------------------------------------------------------------------
template<class PK_t>
void StrongMPC<PK_t>::setup(const Teuchos::Ptr<State>& S) {
  // tweak the sub-PK parameter lists
  Teuchos::RCP<Teuchos::ParameterList> pks_list = Teuchos::sublist(plist_, "PKs");
  for (Teuchos::ParameterList::ConstIterator param=pks_list->begin();
       param!=pks_list->end(); ++param) {
    std::string pname = param->first;
    if (pks_list->isSublist(pname)) {
      pks_list->sublist(pname).set("strongly coupled PK", true);
    } else {
      ASSERT(0);
    }
  }

  MPC<PK_t>::setup(S);
  PKBDFBase::setup(S);

  // Set the initial timestep as the min of the sub-pk sizes.
  dt_ = 1.0e99;
  for (typename MPC<PK_t>::SubPKList::iterator pk = MPC<PK_t>::sub_pks_.begin();
       pk != MPC<PK_t>::sub_pks_.end(); ++pk) {
    dt_ = std::min<double>(dt_, (*pk)->get_dt());
  }

};

// -----------------------------------------------------------------------------
// Required unique initialize(), just calls both of its base class
// initialize() methods.
// -----------------------------------------------------------------------------
template<class PK_t>
void StrongMPC<PK_t>::initialize(const Teuchos::Ptr<State>& S) {
  // Just calls both subclass's initialize.  NOTE - order is important here --
  // MPC<PK_t> grabs the primary variables from each sub-PK and stuffs
  // them into the solution, which must be done prior to BDFBase initializing
  // the timestepper.

  // Initialize all sub PKs.
  MPC<PK_t>::initialize(S);

  // Initialize my timestepper.
  PKBDFBase::initialize(S);
};

// -----------------------------------------------------------------------------
// Compute the non-linear functional g = g(t,u,udot).
// -----------------------------------------------------------------------------
template<class PK_t>
void StrongMPC<PK_t>::Functional(double t_old, double t_new, Teuchos::RCP<TreeVector> u_old,
                    Teuchos::RCP<TreeVector> u_new, Teuchos::RCP<TreeVector> g) {
  solution_to_state(u_new, S_next_);

  // loop over sub-PKs
  for (unsigned int i=0; i!=sub_pks_.size(); ++i) {
    // pull out the old solution sub-vector
    Teuchos::RCP<TreeVector> pk_u_old(Teuchos::null);
    if (u_old != Teuchos::null) {
      pk_u_old = u_old->SubVector(i);
      if (pk_u_old == Teuchos::null) {
        Errors::Message message("MPC: vector structure does not match PK structure");
        Exceptions::amanzi_throw(message);
      }
    }

    // pull out the new solution sub-vector
    Teuchos::RCP<TreeVector> pk_u_new = u_new->SubVector(i);
    if (pk_u_new == Teuchos::null) {
      Errors::Message message("MPC: vector structure does not match PK structure");
      Exceptions::amanzi_throw(message);
    }

    // pull out the residual sub-vector
    Teuchos::RCP<TreeVector> pk_g = g->SubVector(i);
    if (pk_g == Teuchos::null) {
      Errors::Message message("MPC: vector structure does not match PK structure");
      Exceptions::amanzi_throw(message);
    }

    // fill the nonlinear function with each sub-PKs contribution
    sub_pks_[i]->Functional(t_old, t_new, pk_u_old, pk_u_new, pk_g);
  }
};

// -----------------------------------------------------------------------------
// Applies preconditioner to u and returns the result in Pu.
// -----------------------------------------------------------------------------
template<class PK_t>
void StrongMPC<PK_t>::ApplyPreconditioner(Teuchos::RCP<const TreeVector> u, Teuchos::RCP<TreeVector> Pu) {
  // loop over sub-PKs
  for (unsigned int i=0; i!=sub_pks_.size(); ++i) {
    // pull out the u sub-vector
    Teuchos::RCP<const TreeVector> pk_u = u->SubVector(i);
    if (pk_u == Teuchos::null) {
      Errors::Message message("MPC: vector structure does not match PK structure");
      Exceptions::amanzi_throw(message);
    }

    // pull out the preconditioned u sub-vector
    Teuchos::RCP<TreeVector> pk_Pu = Pu->SubVector(i);
    if (pk_Pu == Teuchos::null) {
      Errors::Message message("MPC: vector structure does not match PK structure");
      Exceptions::amanzi_throw(message);
    }

    // Fill the preconditioned u as the block-diagonal product using each sub-PK.
    sub_pks_[i]->ApplyPreconditioner(pk_u, pk_Pu);
  }

//   std::cout<<*(((Pu->SubVector("flow"))->Data())->ViewComponent("cell", false));
//   cout<<"Exit from StrongMPC precon\n";
//   exit(0);
};

// -----------------------------------------------------------------------------
// Compute a norm on u-du and returns the result.
// For a Strong MPC, the enorm is just the max of the sub PKs enorms.
// -----------------------------------------------------------------------------
template<class PK_t>
double StrongMPC<PK_t>::ErrorNorm(Teuchos::RCP<const TreeVector> u,
                        Teuchos::RCP<const TreeVector> du){
  double norm = 0.0;

  // loop over sub-PKs
  for (unsigned int i=0; i!=sub_pks_.size(); ++i) {
    // pull out the u sub-vector
    Teuchos::RCP<const TreeVector> pk_u = u->SubVector(i);
    if (pk_u == Teuchos::null) {
      Errors::Message message("MPC: vector structure does not match PK structure");
      Exceptions::amanzi_throw(message);
    }

    // pull out the du sub-vector
    Teuchos::RCP<const TreeVector> pk_du = du->SubVector(i);
    if (pk_du == Teuchos::null) {
      Errors::Message message("MPC: vector structure does not match PK structure");
      Exceptions::amanzi_throw(message);
    }

    // norm is the max of the sub-PK norms
    double tmp_norm = sub_pks_[i]->ErrorNorm(pk_u, pk_du);
    norm = std::max(norm, tmp_norm);
  }
  return norm;
};

// -----------------------------------------------------------------------------
// Update the preconditioner.
// -----------------------------------------------------------------------------
template<class PK_t>
void StrongMPC<PK_t>::UpdatePreconditioner(double t, Teuchos::RCP<const TreeVector> up, double h) {
  PKDefaultBase::solution_to_state(up, S_next_);

  // loop over sub-PKs
  for (unsigned int i=0; i!=sub_pks_.size(); ++i) {
    // pull out the up sub-vector
    Teuchos::RCP<const TreeVector> pk_up = up->SubVector(i);
    if (pk_up == Teuchos::null) {
      Errors::Message message("MPC: vector structure does not match PK structure");
      Exceptions::amanzi_throw(message);
    }

    // update precons of each of the sub-PKs
    sub_pks_[i]->UpdatePreconditioner(t, pk_up, h);
  };
};

// -----------------------------------------------------------------------------
// Experimental approach -- calling this indicates that the time integration
// scheme is changing the value of the solution in state.
// -----------------------------------------------------------------------------
template<class PK_t>
void StrongMPC<PK_t>::ChangedSolution() {
  // loop over sub-PKs
  for (typename MPC<PK_t>::SubPKList::iterator pk = MPC<PK_t>::sub_pks_.begin();
       pk != MPC<PK_t>::sub_pks_.end(); ++pk) {
    (*pk)->ChangedSolution();
  }
};

// -----------------------------------------------------------------------------
// Check admissibility of each sub-pk
// -----------------------------------------------------------------------------
template<class PK_t>
bool StrongMPC<PK_t>::IsAdmissible(Teuchos::RCP<const TreeVector> u) {
  // First ensure each PK thinks we are admissible -- this will ensure
  // the residual can at least be evaluated.
  for (unsigned int i=0; i!=sub_pks_.size(); ++i) {
    // pull out the u sub-vector
    Teuchos::RCP<const TreeVector> pk_u = u->SubVector(i);
    if (pk_u == Teuchos::null) {
      Errors::Message message("MPC: vector structure does not match PK structure");
      Exceptions::amanzi_throw(message);
    }

    if (!sub_pks_[i]->IsAdmissible(pk_u)) {
      if (vo_->os_OK(Teuchos::VERB_HIGH))
        *vo_->os() << "PK " << sub_pks_[i]->name() << " is not admissible." << std::endl;

      return false;
    }
  }

  // If that worked, check backtracking admissility.
  return PKBDFBase::IsAdmissible(u);
};

// -----------------------------------------------------------------------------
// Modify predictor from each sub pk.
// -----------------------------------------------------------------------------
template<class PK_t>
bool StrongMPC<PK_t>::ModifyPredictor(double h, Teuchos::RCP<const TreeVector> u0,
        Teuchos::RCP<TreeVector> u) {
  // loop over sub-PKs
  bool modified = false;
  for (unsigned int i=0; i!=sub_pks_.size(); ++i) {
    // pull out the u sub-vector
    Teuchos::RCP<const TreeVector> pk_u0 = u0->SubVector(i);
    Teuchos::RCP<TreeVector> pk_u = u->SubVector(i);
    if (pk_u == Teuchos::null) {
      Errors::Message message("MPC: vector structure does not match PK structure");
      Exceptions::amanzi_throw(message);
    }

    modified |= sub_pks_[i]->ModifyPredictor(h, pk_u0, pk_u);
  }
  return modified;
};

// -----------------------------------------------------------------------------
// Modify correction from each sub pk.
// -----------------------------------------------------------------------------
template<class PK_t>
ModifyCorrectionResult StrongMPC<PK_t>::ModifyCorrection(double h,
        Teuchos::RCP<const TreeVector> res,
        Teuchos::RCP<const TreeVector> u, Teuchos::RCP<TreeVector> du) {
  // loop over sub-PKs
  ModifyCorrectionResult modified = AmanziSolvers::CORRECTION_NOT_MODIFIED;
  for (unsigned int i=0; i!=sub_pks_.size(); ++i) {
    // pull out the u sub-vector
    Teuchos::RCP<const TreeVector> pk_u = u->SubVector(i);
    Teuchos::RCP<const TreeVector> pk_res = res->SubVector(i);
    Teuchos::RCP<TreeVector> pk_du = du->SubVector(i);

    if (pk_u == Teuchos::null || pk_du == Teuchos::null) {
      Errors::Message message("MPC: vector structure does not match PK structure");
      Exceptions::amanzi_throw(message);
    }

    modified = std::max(modified, sub_pks_[i]->ModifyCorrection(h, pk_res, pk_u, pk_du));
  }
  return modified;
};

} // close namespace Amanzi

#endif
<|endoftext|>


<commit_before>/*=====================================================================

 QGroundControl Open Source Ground Control Station

 (c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>

 This file is part of the QGROUNDCONTROL project

 QGROUNDCONTROL is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 QGROUNDCONTROL is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.

 ======================================================================*/

/// @file
///     @author Thomas Gubler <thomasgubler@gmail.com>

#include "QGCMapRCToParamDialog.h"
#include "ui_QGCMapRCToParamDialog.h"

#include <QDebug>
#include <QTimer>
#include <QEventLoop>
#include <QShowEvent>
#include <QPushButton>

QGCMapRCToParamDialog::QGCMapRCToParamDialog(QString param_id,
        UASInterface *mav, QWidget *parent) :
    QDialog(parent),
    ui(new Ui::QGCMapRCToParamDialog),
    param_id(param_id),
    mav(mav)
{
    ui->setupUi(this);

    // only enable ok button when param was refreshed
    QPushButton *okButton = ui->buttonBox->button(QDialogButtonBox::Ok);
    okButton->setEnabled(false);

    ui->paramIdLabel->setText(param_id);

    // Refresh the param
    ParamLoader *paramLoader = new ParamLoader(param_id, mav);
    paramLoader->moveToThread(&paramLoadThread);
    connect(&paramLoadThread, &QThread::finished, paramLoader, &QObject::deleteLater);
    connect(this, &QGCMapRCToParamDialog::refreshParam, paramLoader, &ParamLoader::load);
    connect(paramLoader, &ParamLoader::paramLoaded, this, &QGCMapRCToParamDialog::paramLoaded);
    paramLoadThread.start();
    emit refreshParam();
}

QGCMapRCToParamDialog::~QGCMapRCToParamDialog()
{
    delete ui;
}

void QGCMapRCToParamDialog::accept() {
    emit mapRCToParamDialogResult(param_id,
            (float)ui->scaleDoubleSpinBox->value(),
            (float)ui->value0DoubleSpinBox->value(),
            (quint8)ui->rcParamChannelComboBox->currentIndex(),
            (float)ui->minValueDoubleSpinBox->value(),
            (float)ui->maxValueDoubleSpinBox->value());

    QDialog::accept();
}

void QGCMapRCToParamDialog::paramLoaded(bool success, float value, QString message)
{
    paramLoadThread.quit();
    if (success) {
        ui->infoLabel->setText("Parameter value is up to date");
        ui->value0DoubleSpinBox->setValue(value);
        ui->value0DoubleSpinBox->setEnabled(true);

        connect(this, &QGCMapRCToParamDialog::mapRCToParamDialogResult,
                mav, &UASInterface::sendMapRCToParam);
        QPushButton *okButton = ui->buttonBox->button(QDialogButtonBox::Ok);
        okButton->setEnabled(true);
    } else {
        qDebug() << "Error while reading param" << param_id;
        ui->infoLabel->setText("Error while refreshing param (" + message + ")");
    }
}

void ParamLoader::load()
{
        // refresh the parameter from onboard to make sure the current value is used
        paramMgr->requestParameterUpdate(paramMgr->getDefaultComponentId(), param_id);

        // wait until parameter update is received
        QEventLoop loop;
        QTimer timer;
        connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
        timer.start(10 * 1e3);
        // overloaded signal:
        connect(mav, static_cast<void (UASInterface::*)(
                    int, int, QString, QVariant)>(&UASInterface::parameterChanged),
                this, &ParamLoader::handleParameterChanged);
        connect(this, &ParamLoader::correctParameterChanged,
                &loop, &QEventLoop::quit);
        loop.exec();

        if (!param_received == true) {
            // timeout
            emit paramLoaded(false, 0.0f, "Timeout");
            return;
        }

        QVariant current_param_value;
        bool got_param = paramMgr->getParameterValue(paramMgr->getDefaultComponentId(),
                param_id, current_param_value);

        QString message = got_param ? "" : "param manager Error";
        emit paramLoaded(got_param, current_param_value.toFloat(), message);
}

void ParamLoader::handleParameterChanged(int uas, int component, QString parameterName, QVariant value)
{
    if (uas == mav->getUASID() && parameterName == param_id) {
        param_received = true;
        emit correctParameterChanged();
    }
}
<commit_msg>fix reorder and unused errors<commit_after>/*=====================================================================

 QGroundControl Open Source Ground Control Station

 (c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>

 This file is part of the QGROUNDCONTROL project

 QGROUNDCONTROL is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 QGROUNDCONTROL is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.

 ======================================================================*/

/// @file
///     @author Thomas Gubler <thomasgubler@gmail.com>

#include "QGCMapRCToParamDialog.h"
#include "ui_QGCMapRCToParamDialog.h"

#include <QDebug>
#include <QTimer>
#include <QEventLoop>
#include <QShowEvent>
#include <QPushButton>

QGCMapRCToParamDialog::QGCMapRCToParamDialog(QString param_id,
        UASInterface *mav, QWidget *parent) :
    QDialog(parent),
    param_id(param_id),
    mav(mav),
    ui(new Ui::QGCMapRCToParamDialog)
{
    ui->setupUi(this);

    // only enable ok button when param was refreshed
    QPushButton *okButton = ui->buttonBox->button(QDialogButtonBox::Ok);
    okButton->setEnabled(false);

    ui->paramIdLabel->setText(param_id);

    // Refresh the param
    ParamLoader *paramLoader = new ParamLoader(param_id, mav);
    paramLoader->moveToThread(&paramLoadThread);
    connect(&paramLoadThread, &QThread::finished, paramLoader, &QObject::deleteLater);
    connect(this, &QGCMapRCToParamDialog::refreshParam, paramLoader, &ParamLoader::load);
    connect(paramLoader, &ParamLoader::paramLoaded, this, &QGCMapRCToParamDialog::paramLoaded);
    paramLoadThread.start();
    emit refreshParam();
}

QGCMapRCToParamDialog::~QGCMapRCToParamDialog()
{
    delete ui;
}

void QGCMapRCToParamDialog::accept() {
    emit mapRCToParamDialogResult(param_id,
            (float)ui->scaleDoubleSpinBox->value(),
            (float)ui->value0DoubleSpinBox->value(),
            (quint8)ui->rcParamChannelComboBox->currentIndex(),
            (float)ui->minValueDoubleSpinBox->value(),
            (float)ui->maxValueDoubleSpinBox->value());

    QDialog::accept();
}

void QGCMapRCToParamDialog::paramLoaded(bool success, float value, QString message)
{
    paramLoadThread.quit();
    if (success) {
        ui->infoLabel->setText("Parameter value is up to date");
        ui->value0DoubleSpinBox->setValue(value);
        ui->value0DoubleSpinBox->setEnabled(true);

        connect(this, &QGCMapRCToParamDialog::mapRCToParamDialogResult,
                mav, &UASInterface::sendMapRCToParam);
        QPushButton *okButton = ui->buttonBox->button(QDialogButtonBox::Ok);
        okButton->setEnabled(true);
    } else {
        qDebug() << "Error while reading param" << param_id;
        ui->infoLabel->setText("Error while refreshing param (" + message + ")");
    }
}

void ParamLoader::load()
{
        // refresh the parameter from onboard to make sure the current value is used
        paramMgr->requestParameterUpdate(paramMgr->getDefaultComponentId(), param_id);

        // wait until parameter update is received
        QEventLoop loop;
        QTimer timer;
        connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
        timer.start(10 * 1e3);
        // overloaded signal:
        connect(mav, static_cast<void (UASInterface::*)(
                    int, int, QString, QVariant)>(&UASInterface::parameterChanged),
                this, &ParamLoader::handleParameterChanged);
        connect(this, &ParamLoader::correctParameterChanged,
                &loop, &QEventLoop::quit);
        loop.exec();

        if (!param_received == true) {
            // timeout
            emit paramLoaded(false, 0.0f, "Timeout");
            return;
        }

        QVariant current_param_value;
        bool got_param = paramMgr->getParameterValue(paramMgr->getDefaultComponentId(),
                param_id, current_param_value);

        QString message = got_param ? "" : "param manager Error";
        emit paramLoaded(got_param, current_param_value.toFloat(), message);
}

void ParamLoader::handleParameterChanged(int uas, int component, QString parameterName, QVariant value)
{
    Q_UNUSED(component);
    Q_UNUSED(value);
    if (uas == mav->getUASID() && parameterName == param_id) {
        param_received = true;
        emit correctParameterChanged();
    }
}
<|endoftext|>