<commit_before>import numpy as _np
from sklearn.linear_model import LinearRegression as _LinearRegression

from .base import BaseModel as _BaseModel
from .utils import X_fitter as X_fitter
from .utils import X_predictor as X_predictor

# Suppress an annoying error from scikit-learn
import warnings
warnings.filterwarnings(action="ignore", module="scipy",
                        message="^internal gelsd")

class EpistasisLinearRegression(_LinearRegression, _BaseModel):
    """Ordinary least-squares regression for estimating high-order, epistatic
    interactions in a genotype-phenotype map.

    Methods are described in the following publication:
        Sailer, Z. R. & Harms, M. J. 'Detecting High-Order Epistasis in
        Nonlinear Genotype-Phenotype Maps'. Genetics 205, 1079-1088 (2017).

    Parameters
    ----------
    order : int
        order of epistasis
    model_type : str (default="global")
        model matrix type. See publication above for more information
    """

    def __init__(self, order=1, model_type="global", n_jobs=1, **kwargs):
        # Set Linear Regression settings.
        self.fit_intercept = False
        self.normalize = False
        self.copy_X = False
        self.n_jobs = n_jobs
        self.set_params(model_type=model_type, order=order)
        self.Xbuilt = {}

        # Store model specs.
        self.model_specs = dict(
            order=self.order,
            model_type=self.model_type,
            **kwargs)

    @X_fitter
    def fit(self, X='obs', y='obs', sample_weight=None, **kwargs):
        # If a threshold exists in the data, pre-classify genotypes
        return super(self.__class__, self).fit(X, y,
                                               sample_weight=sample_weight)

    @X_predictor
    def predict(self, X='complete'):
        return super(self.__class__, self).predict(X)

    @X_fitter
    def score(self, X='obs', y='obs', sample_weight=None):
        return super(self.__class__, self).score(X, y,
                                                 sample_weight=sample_weight)

    @property
    def thetas(self):
        return self.coef_

    @X_predictor
    def hypothesis(self, X='complete', thetas=None):
        """Given a set of parameters, compute a set of phenotypes. This is method
        can be used to test a set of parameters (Useful for bayesian sampling).
        """
        if thetas is None:
            thetas = self.thetas
        return _np.dot(X, thetas)

    @X_fitter
    def lnlike_of_data(self,
                       X="obs", y="obs",
                       yerr="obs",
                       sample_weight=None,
                       thetas=None):
        """Calculate the log likelihoods of each data point, given a set of
        model coefficients.

        Parameters
        ----------
        X : 2d array
            model matrix
        y : array
            data to calculate the likelihood
        yerr: array
            uncertainty in data
        thetas : array
            array of model coefficients

        Returns
        -------
        lnlike : np.ndarray
            log-likelihood of each data point given a model.
        """
        # If thetas are not explicitly named, get them from the model
        if thetas is None:
            thetas = self.thetas

        # Handle yerr.
        # Check if yerr is string
        if type(yerr) is str and yerr in ["obs", "complete"]:
            yerr = self.gpm.binary.std.upper

        # Else, numpy array or dataframe
        elif type(y) != np.array and type(y) != pd.Series:
            raise FittingError("yerr is not valid. Must be one of the "
                               "following: 'obs', 'complete', "
                               "numpy.array, pandas.Series. Right now, "
                               "its {}".format(type(yerr)))

        # Calculate y from model.
        ymodel = self.hypothesis(X=X, thetas=thetas)
        return (- 0.5 * _np.log(2 * _np.pi * yerr**2) -
                (0.5 * ((y - ymodel)**2 / yerr**2)))
<commit_msg>add lasso<commit_after>import numpy as _np
from sklearn.linear_model import LinearRegression as _LinearRegression
from sklearn.linear_model import Lasso as _Lasso

from .base import BaseModel as _BaseModel
from .utils import X_fitter as X_fitter
from .utils import X_predictor as X_predictor
from .utils import sklearn_to_epistasis

# Suppress an annoying error from scikit-learn
import warnings
warnings.filterwarnings(action="ignore", module="scipy",
                        message="^internal gelsd")

class EpistasisLinearRegression(_LinearRegression, _BaseModel):
    """Ordinary least-squares regression for estimating high-order, epistatic
    interactions in a genotype-phenotype map.

    Methods are described in the following publication:
        Sailer, Z. R. & Harms, M. J. 'Detecting High-Order Epistasis in
        Nonlinear Genotype-Phenotype Maps'. Genetics 205, 1079-1088 (2017).

    Parameters
    ----------
    order : int
        order of epistasis
    model_type : str (default="global")
        model matrix type. See publication above for more information
    """

    def __init__(self, order=1, model_type="global", n_jobs=1, **kwargs):
        # Set Linear Regression settings.
        self.fit_intercept = False
        self.normalize = False
        self.copy_X = False
        self.n_jobs = n_jobs
        self.set_params(model_type=model_type, order=order)
        self.Xbuilt = {}

        # Store model specs.
        self.model_specs = dict(
            order=self.order,
            model_type=self.model_type,
            **kwargs)

    @X_fitter
    def fit(self, X='obs', y='obs', sample_weight=None, **kwargs):
        # If a threshold exists in the data, pre-classify genotypes
        return super(self.__class__, self).fit(X, y,
                                               sample_weight=sample_weight)

    @X_predictor
    def predict(self, X='complete'):
        return super(self.__class__, self).predict(X)

    @X_fitter
    def score(self, X='obs', y='obs', sample_weight=None):
        return super(self.__class__, self).score(X, y,
                                                 sample_weight=sample_weight)

    @property
    def thetas(self):
        return self.coef_

    @X_predictor
    def hypothesis(self, X='complete', thetas=None):
        """Given a set of parameters, compute a set of phenotypes. This is method
        can be used to test a set of parameters (Useful for bayesian sampling).
        """
        if thetas is None:
            thetas = self.thetas
        return _np.dot(X, thetas)

    @X_fitter
    def lnlike_of_data(self,
                       X="obs", y="obs",
                       yerr="obs",
                       sample_weight=None,
                       thetas=None):
        """Calculate the log likelihoods of each data point, given a set of
        model coefficients.

        Parameters
        ----------
        X : 2d array
            model matrix
        y : array
            data to calculate the likelihood
        yerr: array
            uncertainty in data
        thetas : array
            array of model coefficients

        Returns
        -------
        lnlike : np.ndarray
            log-likelihood of each data point given a model.
        """
        # If thetas are not explicitly named, get them from the model
        if thetas is None:
            thetas = self.thetas

        # Handle yerr.
        # Check if yerr is string
        if type(yerr) is str and yerr in ["obs", "complete"]:
            yerr = self.gpm.binary.std.upper

        # Else, numpy array or dataframe
        elif type(y) != np.array and type(y) != pd.Series:
            raise FittingError("yerr is not valid. Must be one of the "
                               "following: 'obs', 'complete', "
                               "numpy.array, pandas.Series. Right now, "
                               "its {}".format(type(yerr)))

        # Calculate y from model.
        ymodel = self.hypothesis(X=X, thetas=thetas)
        return (- 0.5 * _np.log(2 * _np.pi * yerr**2) -
                (0.5 * ((y - ymodel)**2 / yerr**2)))

class EpistasisLasso(_Lasso, _BaseModel):
    """"""
    def __init__(self, order=1, model_type="global", alpha=1.0,
                 precompute=False, max_iter=1000, tol=0.0001,
                 warm_start=False, positive=False, random_state=None,
                 selection='cyclic', **kwargs):
        # Set Linear Regression settings.
        self.fit_intercept = False
        self.normalize = False
        self.copy_X = True
        self.alpha = alpha
        self.precompute = precompute
        self.max_iter = max_iter
        self.tol = tol
        self.warm_start = warm_start
        self.positive = positive
        self.random_state = random_state
        self.selection = selection
        self.l1_ratio = 1.0

        self.set_params(model_type=model_type, order=order)
        self.Xbuilt = {}

        # Store model specs.
        self.model_specs = dict(
            order=self.order,
            model_type=self.model_type,
            **kwargs)

    @X_fitter
    def fit(self, X='obs', y='obs', sample_weight=None, **kwargs):
        # If a threshold exists in the data, pre-classify genotypes
        X = _np.asfortranarray(X)
        return super(self.__class__, self).fit(X, y, sample_weight)

    @X_predictor
    def predict(self, X='complete'):
        X = _np.asfortranarray(X)
        return super(self.__class__, self).predict(X)

    @X_fitter
    def score(self, X='obs', y='obs'):
        X = _np.asfortranarray(X)
        return super(self.__class__, self).score(X, y)

    @property
    def thetas(self):
        return self.coef_
<|endoftext|>


<commit_before>from datetime import datetime, timedelta
from tapiriik.database import cachedb
import requests
import hashlib
import pytz
import warnings

class ActivityType:  # taken from RK API docs. The text values have no meaning except for debugging
    Running = "Running"
    Cycling = "Cycling"
    MountainBiking = "MtnBiking"
    Walking = "Walking"
    Hiking = "Hiking"
    DownhillSkiing = "DownhillSkiing"
    CrossCountrySkiing = "XCSkiing"
    Snowboarding = "Snowboarding"
    Skating = "Skating"
    Swimming = "Swimming"
    Wheelchair = "Wheelchair"
    Rowing = "Rowing"
    Elliptical = "Elliptical"
    Other = "Other"

    # The right-most element is the "most specific."
    _hierarchy = [
        [Cycling, MountainBiking],
        [Running, Walking, Hiking]
    ]
    def PickMostSpecific(types):
        types = [x for x in types if x and x is not ActivityType.Other]
        if len(types) == 0:
            return ActivityType.Other
        most_specific = types[0]
        for definition in ActivityType._hierarchy:
            if len([x for x in types if x in definition]) == len(types):
                for act_type in types:
                    if definition.index(most_specific) < definition.index(act_type):
                        most_specific = act_type
        return most_specific

class Activity:
    ImplicitPauseTime = timedelta(minutes=1, seconds=5)

    def __init__(self, startTime=None, endTime=None, actType=ActivityType.Other, distance=None, name=None, tz=None, waypointList=None, private=False):
        self.StartTime = startTime
        self.EndTime = endTime
        self.Type = actType
        self.Waypoints = waypointList if waypointList is not None else []
        self.Distance = distance
        self.TZ = tz
        self.Name = name
        self.Private = private
        self.PrerenderedFormats = {}

    def CalculateUID(self):
        if not self.StartTime:
            return  # don't even try
        csp = hashlib.new("md5")
        roundedStartTime = self.StartTime
        roundedStartTime = roundedStartTime - timedelta(microseconds=roundedStartTime.microsecond)
        if self.TZ:
            roundedStartTime = roundedStartTime.astimezone(self.TZ)
        csp.update(roundedStartTime.strftime("%Y-%m-%d %H:%M:%S").encode('utf-8'))  # exclude TZ for compat
        self.UID = csp.hexdigest()

    def DefineTZ(self):
        """ run localize() on all contained dates (doesn't change values) """
        if self.TZ is None:
            raise ValueError("TZ not set")
        if self.StartTime and self.StartTime.tzinfo is None:
            self.StartTime = self.TZ.localize(self.StartTime)
        if self.EndTime and self.EndTime.tzinfo is None:
            self.EndTime = self.TZ.localize(self.EndTime)
        for wp in self.Waypoints:
            if wp.Timestamp.tzinfo is None:
                wp.Timestamp = self.TZ.localize(wp.Timestamp)
        self.CalculateUID()

    def AdjustTZ(self):
        """ run astimezone() on all contained dates (requires non-naive DTs) """
        if self.TZ is None:
            raise ValueError("TZ not set")
        self.StartTime = self.StartTime.astimezone(self.TZ)
        self.EndTime = self.EndTime.astimezone(self.TZ)

        for wp in self.Waypoints:
                wp.Timestamp = wp.Timestamp.astimezone(self.TZ)
        self.CalculateUID()

    def CalculateTZ(self, loc=None):
        if len(self.Waypoints) == 0 and loc is None:
            raise Exception("Can't find TZ without waypoints")
        if loc is None:
            for wp in self.Waypoints:
                if wp.Location is not None and wp.Location.Latitude is not None and wp.Location.Longitude is not None:
                    loc = wp.Location
                    break
            if loc is None:
                raise Exception("Can't find TZ without a waypoint with a location")
        cachedTzData = cachedb.tz_cache.find_one({"Latitude": loc.Latitude, "Longitude": loc.Longitude})
        if cachedTzData is None:
            warnings.filterwarnings("ignore", "the 'strict' argument")
            warnings.filterwarnings("ignore", "unclosed <socket")
            resp = requests.get("http://api.geonames.org/timezoneJSON?username=tapiriik&radius=0.5&lat=" + str(loc.Latitude) + "&lng=" + str(loc.Longitude))
            data = resp.json()
            cachedTzData = {}
            if "timezoneId" in data:
                cachedTzData["TZ"] = data["timezoneId"]
            else:
                cachedTzData["TZ"] = data["rawOffset"]
            cachedTzData["Latitude"] = loc.Latitude
            cachedTzData["Longitude"] = loc.Longitude
            cachedb.tz_cache.insert(cachedTzData)

        if type(cachedTzData["TZ"]) != str:
            self.TZ = pytz.FixedOffset(cachedTzData["TZ"] * 60)
        else:
            self.TZ = pytz.timezone(cachedTzData["TZ"])
        return self.TZ

    def EnsureTZ(self):
        self.CalculateTZ()
        if self.StartTime.tzinfo is None:
            self.DefineTZ()
        else:
            self.AdjustTZ()

    def CalculateDistance(self):
        self.Distance = self.GetDistance()

    def GetDistance(self, startWpt=None, endWpt=None):
        import math
        dist = 0
        altHold = None  # seperate from the lastLoc variable, since we want to hold the altitude as long as required
        lastTimestamp = lastLoc = None

        if not startWpt:
            startWpt = self.Waypoints[0]
        if not endWpt:
            endWpt = self.Waypoints[-1]

        for x in range(self.Waypoints.index(startWpt), self.Waypoints.index(endWpt) + 1):
            timeDelta = self.Waypoints[x].Timestamp - lastTimestamp if lastTimestamp else None
            lastTimestamp = self.Waypoints[x].Timestamp

            if self.Waypoints[x].Type == WaypointType.Pause or (timeDelta and timeDelta > self.ImplicitPauseTime):
                lastLoc = None  # don't count distance while paused
                continue

            loc = self.Waypoints[x].Location
            if loc is None or loc.Longitude is None or loc.Latitude is None:
                # Used to throw an exception in this case, but the TCX schema allows for location-free waypoints, so we'll just patch over it.
                continue

            if loc and lastLoc:
                altHold = lastLoc.Altitude if lastLoc.Altitude is not None else altHold
                latRads = loc.Latitude * math.pi / 180
                meters_lat_degree = 1000 * 111.13292 + 1.175 * math.cos(4 * latRads) - 559.82 * math.cos(2 * latRads)
                meters_lon_degree = 1000 * 111.41284 * math.cos(latRads) - 93.5 * math.cos(3 * latRads)
                dx = (loc.Longitude - lastLoc.Longitude) * meters_lon_degree
                dy = (loc.Latitude - lastLoc.Latitude) * meters_lat_degree
                if loc.Altitude is not None and altHold is not None:  # incorporate the altitude when possible
                    dz = loc.Altitude - altHold
                else:
                    dz = 0
                dist += math.sqrt(dx ** 2 + dy ** 2 + dz ** 2)
            lastLoc = loc

        return dist

    def GetDuration(self, startWpt=None, endWpt=None):
        if len(self.Waypoints) < 3:
            # Either no waypoints, or one at the start and one at the end - just use regular time elapsed
            return self.EndTime - self.StartTime
        duration = timedelta(0)
        if not startWpt:
            startWpt = self.Waypoints[0]
        if not endWpt:
            endWpt = self.Waypoints[-1]
        lastTimestamp = None
        for x in range(self.Waypoints.index(startWpt), self.Waypoints.index(endWpt) + 1):
            wpt = self.Waypoints[x]
            delta = wpt.Timestamp - lastTimestamp if lastTimestamp else None
            lastTimestamp = wpt.Timestamp
            if wpt.Type is WaypointType.Pause:
                lastTimestamp = None
            elif delta and delta > self.ImplicitPauseTime:
                delta = None  # Implicit pauses
            if delta:
                duration += delta
        if duration.total_seconds() == 0:
            raise ValueError("Zero-duration activity")
        return duration

    def CheckSanity(self):
        if not hasattr(self, "UploadedTo") or len(self.UploadedTo) == 0:
            raise ValueError("Unset UploadedTo field")
        srcs = self.UploadedTo  # this is just so I can see the source of the activity in the exception message
        if self.TZ and self.TZ.utcoffset(self.StartTime.replace(tzinfo=None)) != self.StartTime.tzinfo.utcoffset(self.StartTime.replace(tzinfo=None)):
            raise ValueError("Inconsistent timezone between StartTime (" + str(self.StartTime) + ") and activity (" + str(self.TZ) + ")")
        if self.TZ and self.TZ.utcoffset(self.EndTime.replace(tzinfo=None)) != self.StartTime.tzinfo.utcoffset(self.EndTime.replace(tzinfo=None)):
            raise ValueError("Inconsistent timezone between EndTime (" + str(self.EndTime) + ") and activity (" + str(self.TZ) + ")")
        if len(self.Waypoints) == 0:
            raise ValueError("No waypoints")
        if len(self.Waypoints) == 1:
            raise ValueError("Only one waypoint")
        if self.Distance is not None and self.Distance > 1000 * 1000:
            raise ValueError("Exceedingly long activity (distance)")
        if self.StartTime and self.EndTime:
            # We can only do these checks if the activity has both start and end times (Dropbox)
            if (self.EndTime - self.StartTime).total_seconds() < 0:
                raise ValueError("Event finishes before it starts")
            if (self.EndTime - self.StartTime).total_seconds() == 0:
                raise ValueError("0-duration activity")
            if (self.EndTime - self.StartTime).total_seconds() > 60 * 60 * 24 * 5:
                raise ValueError("Exceedingly long activity (time)")
        altLow = None
        altHigh = None
        pointsWithoutLocation = 0
        for wp in self.Waypoints:
            if self.TZ and self.TZ.utcoffset(wp.Timestamp.replace(tzinfo=None)) != wp.Timestamp.tzinfo.utcoffset(wp.Timestamp.replace(tzinfo=None)):
                raise ValueError("WP " + str(wp.Timestamp) + " and activity timezone (" + str(self.TZ) + ") are inconsistent")
            if wp.Location:
                if wp.Location.Latitude == 0 and wp.Location.Longitude == 0:
                    raise ValueError("Invalid lat/lng")
                if wp.Location.Altitude is not None and (altLow is None or wp.Location.Altitude < altLow):
                    altLow = wp.Location.Altitude
                if wp.Location.Altitude is not None and (altHigh is None or wp.Location.Altitude > altHigh):
                    altHigh = wp.Location.Altitude
            if not wp.Location or wp.Location.Latitude is None or wp.Location.Longitude is None:
                pointsWithoutLocation += 1
        if len(self.Waypoints) - pointsWithoutLocation == 0:
            raise ValueError("No points have location")
        if len(self.Waypoints) - pointsWithoutLocation == 1:
            raise ValueError("Only one point has location")
        if altLow is not None and altLow == altHigh and altLow == 0:  # some activities have very sporadic altitude data, we'll let it be...
            raise ValueError("Invalid altitudes / no change from " + str(altLow))

    def __str__(self):
        return "Activity (" + self.Type + ") Start " + str(self.StartTime) + " " + str(self.StartTime.tzinfo if self.StartTime else "") + " End " + str(self.EndTime) + " " + str(len(self.Waypoints)) + " WPs"
    __repr__ = __str__

    def __eq__(self, other):
        # might need to fix this for TZs?
        return self.StartTime == other.StartTime and self.EndTime == other.EndTime and self.Type == other.Type and self.Waypoints == other.Waypoints and self.Distance == other.Distance and self.Name == other.Name

    def __ne__(self, other):
        return not self.__eq__(other)

class UploadedActivity (Activity):
    pass  # will contain list of which service instances contain this activity - not really merited

class WaypointType:
    Start = 0   # Start of activity
    Regular = 1 # Normal
    Lap = 2     # A new lap starts with this
    Pause = 11  # All waypoints within a paused period should have this type
    Resume = 12 # The first waypoint after a paused period
    End = 100   # End of activity

class Waypoint:
    def __init__(self, timestamp=None, ptType=WaypointType.Regular, location=None, hr=None, power=None, calories=None, cadence=None, temp=None):
        self.Timestamp = timestamp
        self.Location = location
        self.HR = hr
        self.Calories = calories
        self.Power = power  # I doubt there will ever be more parameters than this in terms of interchange
        self.Temp = temp  # never say never
        self.Cadence = cadence  # dammit this better be the last one
        self.Type = ptType

    def __eq__(self, other):
        return self.Timestamp == other.Timestamp and self.Location == other.Location and self.HR == other.HR and self.Calories == other.Calories and self.Temp == other.Temp and self.Cadence == other.Cadence and self.Type == other.Type and self.Power == other.Power

    def __ne__(self, other):
        return not self.__eq__(other)

    def __str__(self):
        if self.Location is None:
            return str(self.Type)+"@"+str(self.Timestamp)
        return str(self.Type) + "@" + str(self.Timestamp) + " " + str(self.Location.Latitude) + "|" + str(self.Location.Longitude) + "^" + str(round(self.Location.Altitude) if self.Location.Altitude is not None else None) + "\n\tHR " + str(self.HR) + " CAD " + str(self.Cadence) + " TEMP " + str(self.Temp) + " PWR " + str(self.Power) + " CAL " + str(self.Calories)
    __repr__ = __str__

class Location:
    def __init__(self, lat, lon, alt):
        self.Latitude = lat
        self.Longitude = lon
        self.Altitude = alt
        self.Datum = "WGS84"  # might eventually need to make this... better

    def __eq__(self, other):
        return self.Latitude == other.Latitude and self.Longitude == other.Longitude and self.Altitude == other.Altitude and self.Datum == other.Datum

    def __ne__(self, other):
        return not self.__eq__(other)
<commit_msg>Fixed interchange Location __eq__ to allow for comparisons to null locations<commit_after>from datetime import datetime, timedelta
from tapiriik.database import cachedb
import requests
import hashlib
import pytz
import warnings

class ActivityType:  # taken from RK API docs. The text values have no meaning except for debugging
    Running = "Running"
    Cycling = "Cycling"
    MountainBiking = "MtnBiking"
    Walking = "Walking"
    Hiking = "Hiking"
    DownhillSkiing = "DownhillSkiing"
    CrossCountrySkiing = "XCSkiing"
    Snowboarding = "Snowboarding"
    Skating = "Skating"
    Swimming = "Swimming"
    Wheelchair = "Wheelchair"
    Rowing = "Rowing"
    Elliptical = "Elliptical"
    Other = "Other"

    # The right-most element is the "most specific."
    _hierarchy = [
        [Cycling, MountainBiking],
        [Running, Walking, Hiking]
    ]
    def PickMostSpecific(types):
        types = [x for x in types if x and x is not ActivityType.Other]
        if len(types) == 0:
            return ActivityType.Other
        most_specific = types[0]
        for definition in ActivityType._hierarchy:
            if len([x for x in types if x in definition]) == len(types):
                for act_type in types:
                    if definition.index(most_specific) < definition.index(act_type):
                        most_specific = act_type
        return most_specific

class Activity:
    ImplicitPauseTime = timedelta(minutes=1, seconds=5)

    def __init__(self, startTime=None, endTime=None, actType=ActivityType.Other, distance=None, name=None, tz=None, waypointList=None, private=False):
        self.StartTime = startTime
        self.EndTime = endTime
        self.Type = actType
        self.Waypoints = waypointList if waypointList is not None else []
        self.Distance = distance
        self.TZ = tz
        self.Name = name
        self.Private = private
        self.PrerenderedFormats = {}

    def CalculateUID(self):
        if not self.StartTime:
            return  # don't even try
        csp = hashlib.new("md5")
        roundedStartTime = self.StartTime
        roundedStartTime = roundedStartTime - timedelta(microseconds=roundedStartTime.microsecond)
        if self.TZ:
            roundedStartTime = roundedStartTime.astimezone(self.TZ)
        csp.update(roundedStartTime.strftime("%Y-%m-%d %H:%M:%S").encode('utf-8'))  # exclude TZ for compat
        self.UID = csp.hexdigest()

    def DefineTZ(self):
        """ run localize() on all contained dates (doesn't change values) """
        if self.TZ is None:
            raise ValueError("TZ not set")
        if self.StartTime and self.StartTime.tzinfo is None:
            self.StartTime = self.TZ.localize(self.StartTime)
        if self.EndTime and self.EndTime.tzinfo is None:
            self.EndTime = self.TZ.localize(self.EndTime)
        for wp in self.Waypoints:
            if wp.Timestamp.tzinfo is None:
                wp.Timestamp = self.TZ.localize(wp.Timestamp)
        self.CalculateUID()

    def AdjustTZ(self):
        """ run astimezone() on all contained dates (requires non-naive DTs) """
        if self.TZ is None:
            raise ValueError("TZ not set")
        self.StartTime = self.StartTime.astimezone(self.TZ)
        self.EndTime = self.EndTime.astimezone(self.TZ)

        for wp in self.Waypoints:
                wp.Timestamp = wp.Timestamp.astimezone(self.TZ)
        self.CalculateUID()

    def CalculateTZ(self, loc=None):
        if len(self.Waypoints) == 0 and loc is None:
            raise Exception("Can't find TZ without waypoints")
        if loc is None:
            for wp in self.Waypoints:
                if wp.Location is not None and wp.Location.Latitude is not None and wp.Location.Longitude is not None:
                    loc = wp.Location
                    break
            if loc is None:
                raise Exception("Can't find TZ without a waypoint with a location")
        cachedTzData = cachedb.tz_cache.find_one({"Latitude": loc.Latitude, "Longitude": loc.Longitude})
        if cachedTzData is None:
            warnings.filterwarnings("ignore", "the 'strict' argument")
            warnings.filterwarnings("ignore", "unclosed <socket")
            resp = requests.get("http://api.geonames.org/timezoneJSON?username=tapiriik&radius=0.5&lat=" + str(loc.Latitude) + "&lng=" + str(loc.Longitude))
            data = resp.json()
            cachedTzData = {}
            if "timezoneId" in data:
                cachedTzData["TZ"] = data["timezoneId"]
            else:
                cachedTzData["TZ"] = data["rawOffset"]
            cachedTzData["Latitude"] = loc.Latitude
            cachedTzData["Longitude"] = loc.Longitude
            cachedb.tz_cache.insert(cachedTzData)

        if type(cachedTzData["TZ"]) != str:
            self.TZ = pytz.FixedOffset(cachedTzData["TZ"] * 60)
        else:
            self.TZ = pytz.timezone(cachedTzData["TZ"])
        return self.TZ

    def EnsureTZ(self):
        self.CalculateTZ()
        if self.StartTime.tzinfo is None:
            self.DefineTZ()
        else:
            self.AdjustTZ()

    def CalculateDistance(self):
        self.Distance = self.GetDistance()

    def GetDistance(self, startWpt=None, endWpt=None):
        import math
        dist = 0
        altHold = None  # seperate from the lastLoc variable, since we want to hold the altitude as long as required
        lastTimestamp = lastLoc = None

        if not startWpt:
            startWpt = self.Waypoints[0]
        if not endWpt:
            endWpt = self.Waypoints[-1]

        for x in range(self.Waypoints.index(startWpt), self.Waypoints.index(endWpt) + 1):
            timeDelta = self.Waypoints[x].Timestamp - lastTimestamp if lastTimestamp else None
            lastTimestamp = self.Waypoints[x].Timestamp

            if self.Waypoints[x].Type == WaypointType.Pause or (timeDelta and timeDelta > self.ImplicitPauseTime):
                lastLoc = None  # don't count distance while paused
                continue

            loc = self.Waypoints[x].Location
            if loc is None or loc.Longitude is None or loc.Latitude is None:
                # Used to throw an exception in this case, but the TCX schema allows for location-free waypoints, so we'll just patch over it.
                continue

            if loc and lastLoc:
                altHold = lastLoc.Altitude if lastLoc.Altitude is not None else altHold
                latRads = loc.Latitude * math.pi / 180
                meters_lat_degree = 1000 * 111.13292 + 1.175 * math.cos(4 * latRads) - 559.82 * math.cos(2 * latRads)
                meters_lon_degree = 1000 * 111.41284 * math.cos(latRads) - 93.5 * math.cos(3 * latRads)
                dx = (loc.Longitude - lastLoc.Longitude) * meters_lon_degree
                dy = (loc.Latitude - lastLoc.Latitude) * meters_lat_degree
                if loc.Altitude is not None and altHold is not None:  # incorporate the altitude when possible
                    dz = loc.Altitude - altHold
                else:
                    dz = 0
                dist += math.sqrt(dx ** 2 + dy ** 2 + dz ** 2)
            lastLoc = loc

        return dist

    def GetDuration(self, startWpt=None, endWpt=None):
        if len(self.Waypoints) < 3:
            # Either no waypoints, or one at the start and one at the end - just use regular time elapsed
            return self.EndTime - self.StartTime
        duration = timedelta(0)
        if not startWpt:
            startWpt = self.Waypoints[0]
        if not endWpt:
            endWpt = self.Waypoints[-1]
        lastTimestamp = None
        for x in range(self.Waypoints.index(startWpt), self.Waypoints.index(endWpt) + 1):
            wpt = self.Waypoints[x]
            delta = wpt.Timestamp - lastTimestamp if lastTimestamp else None
            lastTimestamp = wpt.Timestamp
            if wpt.Type is WaypointType.Pause:
                lastTimestamp = None
            elif delta and delta > self.ImplicitPauseTime:
                delta = None  # Implicit pauses
            if delta:
                duration += delta
        if duration.total_seconds() == 0:
            raise ValueError("Zero-duration activity")
        return duration

    def CheckSanity(self):
        if not hasattr(self, "UploadedTo") or len(self.UploadedTo) == 0:
            raise ValueError("Unset UploadedTo field")
        srcs = self.UploadedTo  # this is just so I can see the source of the activity in the exception message
        if self.TZ and self.TZ.utcoffset(self.StartTime.replace(tzinfo=None)) != self.StartTime.tzinfo.utcoffset(self.StartTime.replace(tzinfo=None)):
            raise ValueError("Inconsistent timezone between StartTime (" + str(self.StartTime) + ") and activity (" + str(self.TZ) + ")")
        if self.TZ and self.TZ.utcoffset(self.EndTime.replace(tzinfo=None)) != self.StartTime.tzinfo.utcoffset(self.EndTime.replace(tzinfo=None)):
            raise ValueError("Inconsistent timezone between EndTime (" + str(self.EndTime) + ") and activity (" + str(self.TZ) + ")")
        if len(self.Waypoints) == 0:
            raise ValueError("No waypoints")
        if len(self.Waypoints) == 1:
            raise ValueError("Only one waypoint")
        if self.Distance is not None and self.Distance > 1000 * 1000:
            raise ValueError("Exceedingly long activity (distance)")
        if self.StartTime and self.EndTime:
            # We can only do these checks if the activity has both start and end times (Dropbox)
            if (self.EndTime - self.StartTime).total_seconds() < 0:
                raise ValueError("Event finishes before it starts")
            if (self.EndTime - self.StartTime).total_seconds() == 0:
                raise ValueError("0-duration activity")
            if (self.EndTime - self.StartTime).total_seconds() > 60 * 60 * 24 * 5:
                raise ValueError("Exceedingly long activity (time)")
        altLow = None
        altHigh = None
        pointsWithoutLocation = 0
        for wp in self.Waypoints:
            if self.TZ and self.TZ.utcoffset(wp.Timestamp.replace(tzinfo=None)) != wp.Timestamp.tzinfo.utcoffset(wp.Timestamp.replace(tzinfo=None)):
                raise ValueError("WP " + str(wp.Timestamp) + " and activity timezone (" + str(self.TZ) + ") are inconsistent")
            if wp.Location:
                if wp.Location.Latitude == 0 and wp.Location.Longitude == 0:
                    raise ValueError("Invalid lat/lng")
                if wp.Location.Altitude is not None and (altLow is None or wp.Location.Altitude < altLow):
                    altLow = wp.Location.Altitude
                if wp.Location.Altitude is not None and (altHigh is None or wp.Location.Altitude > altHigh):
                    altHigh = wp.Location.Altitude
            if not wp.Location or wp.Location.Latitude is None or wp.Location.Longitude is None:
                pointsWithoutLocation += 1
        if len(self.Waypoints) - pointsWithoutLocation == 0:
            raise ValueError("No points have location")
        if len(self.Waypoints) - pointsWithoutLocation == 1:
            raise ValueError("Only one point has location")
        if altLow is not None and altLow == altHigh and altLow == 0:  # some activities have very sporadic altitude data, we'll let it be...
            raise ValueError("Invalid altitudes / no change from " + str(altLow))

    def __str__(self):
        return "Activity (" + self.Type + ") Start " + str(self.StartTime) + " " + str(self.StartTime.tzinfo if self.StartTime else "") + " End " + str(self.EndTime) + " " + str(len(self.Waypoints)) + " WPs"
    __repr__ = __str__

    def __eq__(self, other):
        # might need to fix this for TZs?
        return self.StartTime == other.StartTime and self.EndTime == other.EndTime and self.Type == other.Type and self.Waypoints == other.Waypoints and self.Distance == other.Distance and self.Name == other.Name

    def __ne__(self, other):
        return not self.__eq__(other)

class UploadedActivity (Activity):
    pass  # will contain list of which service instances contain this activity - not really merited

class WaypointType:
    Start = 0   # Start of activity
    Regular = 1 # Normal
    Lap = 2     # A new lap starts with this
    Pause = 11  # All waypoints within a paused period should have this type
    Resume = 12 # The first waypoint after a paused period
    End = 100   # End of activity

class Waypoint:
    def __init__(self, timestamp=None, ptType=WaypointType.Regular, location=None, hr=None, power=None, calories=None, cadence=None, temp=None):
        self.Timestamp = timestamp
        self.Location = location
        self.HR = hr
        self.Calories = calories
        self.Power = power  # I doubt there will ever be more parameters than this in terms of interchange
        self.Temp = temp  # never say never
        self.Cadence = cadence  # dammit this better be the last one
        self.Type = ptType

    def __eq__(self, other):
        return self.Timestamp == other.Timestamp and self.Location == other.Location and self.HR == other.HR and self.Calories == other.Calories and self.Temp == other.Temp and self.Cadence == other.Cadence and self.Type == other.Type and self.Power == other.Power

    def __ne__(self, other):
        return not self.__eq__(other)

    def __str__(self):
        if self.Location is None:
            return str(self.Type)+"@"+str(self.Timestamp)
        return str(self.Type) + "@" + str(self.Timestamp) + " " + str(self.Location.Latitude) + "|" + str(self.Location.Longitude) + "^" + str(round(self.Location.Altitude) if self.Location.Altitude is not None else None) + "\n\tHR " + str(self.HR) + " CAD " + str(self.Cadence) + " TEMP " + str(self.Temp) + " PWR " + str(self.Power) + " CAL " + str(self.Calories)
    __repr__ = __str__

class Location:
    def __init__(self, lat, lon, alt):
        self.Latitude = lat
        self.Longitude = lon
        self.Altitude = alt
        self.Datum = "WGS84"  # might eventually need to make this... better

    def __eq__(self, other):
        if not other:
            return False
        return self.Latitude == other.Latitude and self.Longitude == other.Longitude and self.Altitude == other.Altitude and self.Datum == other.Datum

    def __ne__(self, other):
        return not self.__eq__(other)
<|endoftext|>


<commit_before>"""This module contains the core functionalty of Phaseflow."""
import fenics
import h5py
import numpy
import helpers
import globals
import default
import form
import output

TIME_EPS = 1.e-8

def make_mixed_fe(cell, pressure_degree=default.pressure_degree,
        temperature_degree=default.temperature_degree):
    """ Define the mixed finite element.
    MixedFunctionSpace used to be available but is now deprecated.
    To create the mixed space, I'm using the approach from https://fenicsproject.org/qa/11983/mixedfunctionspace-in-2016-2-0
    """
    velocity_degree = pressure_degree + 1

    velocity_element = fenics.VectorElement('P', cell, velocity_degree)

    pressure_element = fenics.FiniteElement('P', cell, pressure_degree)

    temperature_element = fenics.FiniteElement('P', cell, temperature_degree)

    mixed_element = fenics.MixedElement([velocity_element, pressure_element, temperature_element])

    return mixed_element

def steady(W, w, w_n, steady_relative_tolerance):
    """Check if solution has reached an approximately steady state."""
    steady = False

    time_residual = fenics.Function(W)

    time_residual.assign(w - w_n)

    unsteadiness = fenics.norm(time_residual, 'L2')/fenics.norm(w_n, 'L2')

    helpers.print_once(
        "Unsteadiness (L2 norm of relative time residual), || w_{n+1} || / || w_n || = "+str(unsteadiness))

    if (unsteadiness < steady_relative_tolerance):

        steady = True

    return steady

def run(output_dir = 'output/wang2010_natural_convection_air',
        rayleigh_number = default.parameters['Ra'],
        prandtl_number = default.parameters['Pr'],
        stefan_number = default.parameters['Ste'],
        heat_capacity = default.parameters['C'],
        thermal_conductivity = default.parameters['K'],
        liquid_viscosity = default.parameters['mu_l'],
        solid_viscosity = default.parameters['mu_s'],
        gravity = default.parameters['g'],
        m_B = default.m_B,
        ddT_m_B = default.ddT_m_B,
        penalty_parameter = 1.e-7,
        regularization = default.regularization,
        mesh=default.mesh,
        initial_values_expression = ("0.", "0.", "0.", "0.5*near(x[0],  0.) -0.5*near(x[0],  1.)"),
        boundary_conditions = [{'subspace': 0,
                'value_expression': ("0.", "0."), 'degree': 3,
                'location_expression': "near(x[0],  0.) | near(x[0],  1.) | near(x[1], 0.) | near(x[1],  1.)", 'method': 'topological'},
            {'subspace': 2,
                'value_expression': "0.5", 'degree': 2,
                'location_expression': "near(x[0],  0.)", 'method': 'topological'},
             {'subspace': 2,
                'value_expression': "-0.5", 'degree': 2,
                'location_expression': "near(x[0],  1.)", 'method': 'topological'}],
        start_time = 0.,
        end_time = 10.,
        time_step_size = 1.e-3,
        stop_when_steady = False,
        steady_relative_tolerance=1.e-4,
        adaptive = False,
        adaptive_metric = 'all',
        adaptive_solver_tolerance = 1.e-4,
        nlp_absolute_tolerance = 1.e-8,
        nlp_relative_tolerance = 1.e-8,
        nlp_max_iterations = 50,
        pressure_degree = default.pressure_degree,
        temperature_degree = default.temperature_degree,
        restart = False,
        restart_filepath = ''):
    """Run Phaseflow.

    Phaseflow is configured entirely through the arguments in this run() function.

    See the tests and examples for demonstrations of how to use this.
    """

    # Report inputs.
    helpers.print_once("Running Phaseflow with the following arguments:")

    helpers.print_once(helpers.arguments())

    helpers.mkdir_p(output_dir)

    if fenics.MPI.rank(fenics.mpi_comm_world()) is 0:

        arguments_file = open(output_dir + '/arguments.txt', 'w')

        arguments_file.write(str(helpers.arguments()))

        arguments_file.close()

    # Check if 1D/2D/3D.
    dimensionality = mesh.type().dim()

    helpers.print_once("Running "+str(dimensionality)+"D problem")

    # Initialize time.
    if restart:

        with h5py.File(restart_filepath, 'r') as h5:

            time = h5['t'].value

            assert(abs(time - start_time) < TIME_EPS)

    else:

        time = start_time

    # Define the mixed finite element and the solution function space.
    W_ele = make_mixed_fe(mesh.ufl_cell())

    W = fenics.FunctionSpace(mesh, W_ele)

    # Set the initial values.
    if restart:

        mesh = fenics.Mesh()

        with fenics.HDF5File(mesh.mpi_comm(), restart_filepath, 'r') as h5:

            h5.read(mesh, 'mesh', True)

        W_ele = make_mixed_fe(mesh.ufl_cell())

        W = fenics.FunctionSpace(mesh, W_ele)

        w_n = fenics.Function(W)

        with fenics.HDF5File(mesh.mpi_comm(), restart_filepath, 'r') as h5:

            h5.read(w_n, 'w')

    else:

        w_n = fenics.interpolate(fenics.Expression(initial_values_expression,
            element=W_ele), W)

    # Organize the boundary conditions.
    bcs = []

    for item in boundary_conditions:

        bcs.append(fenics.DirichletBC(W.sub(item['subspace']), item['value_expression'],
            item['location_expression'], method=item['method']))

    # Set the variational form.
    """Set local names for math operators to improve readability."""
    inner, dot, grad, div, sym = fenics.inner, fenics.dot, fenics.grad, fenics.div, fenics.sym

    """The linear, bilinear, and trilinear forms b, a, and c, follow the common notation
    for applying the finite element method to the incompressible Navier-Stokes equations,
    e.g. from danaila2014newton and huerta2003fefluids.
    """
    b = lambda u, q : -div(u)*q  # Divergence

    D = lambda u : sym(grad(u))  # Symmetric part of velocity gradient

    a = lambda mu, u, v : 2.*mu*inner(D(u), D(v))  # Stokes stress-strain

    c = lambda w, z, v : dot(dot(grad(z), w), v)  # Convection of the velocity field

    dt = fenics.Constant(time_step_size)

    Ra = fenics.Constant(rayleigh_number),

    Pr = fenics.Constant(prandtl_number)

    Ste = fenics.Constant(stefan_number)

    C = fenics.Constant(heat_capacity)

    K = fenics.Constant(thermal_conductivity)

    g = fenics.Constant(gravity)

    f_B = lambda T : m_B(T)*g  # Buoyancy force, $f = ma$

    gamma = fenics.Constant(penalty_parameter)

    T_f = fenics.Constant(regularization['T_f'])  # Center of regularized phase-field.

    r = fenics.Constant(regularization['r'])  # Regularization smoothing parameter.

    P = lambda T: 0.5*(1. - fenics.tanh(2.*(T_f - T)/r))  # Regularized phase field.

    mu_l = fenics.Constant(liquid_viscosity)

    mu_s = fenics.Constant(solid_viscosity)

    mu = lambda (T) : mu_s + (mu_l - mu_s)*P(T) # Variable viscosity.

    L = C/Ste  # Latent heat

    u_n, p_n, T_n = fenics.split(w_n)

    w_w = fenics.TrialFunction(W)

    u_w, p_w, T_w = fenics.split(w_w)

    v, q, phi = fenics.TestFunctions(W)

    w_k = fenics.Function(W)

    u_k, p_k, T_k = fenics.split(w_k)

    F = (
        b(u_k, q) - gamma*p_k*q
        + dot(u_k - u_n, v)/dt
        + c(u_k, u_k, v) + b(v, p_k) + a(mu(T_k), u_k, v)
        + dot(f_B(T_k), v)
        + C/dt*(T_k - T_n)*phi
        - dot(C*T_k*u_k, grad(phi))
        + K/Pr*dot(grad(T_k), grad(phi))
        + 1./dt*L*(P(T_k) - P(T_n))*phi
        )*fenics.dx

    ddT_f_B = lambda T : ddT_m_B(T)*g

    sech = lambda theta: 1./fenics.cosh(theta)

    dP = lambda T: sech(2.*(T_f - T)/r)**2/r

    dmu = lambda T : (mu_l - mu_s)*dP(T)

    # Set the Jacobian (formally the Gateaux derivative) in variational form.
    JF = (
        b(u_w, q) - gamma*p_w*q
        + dot(u_w, v)/dt
        + c(u_k, u_w, v) + c(u_w, u_k, v) + b(v, p_w)
        + a(T_w*dmu(T_k), u_k, v) + a(mu(T_k), u_w, v)
        + dot(T_w*ddT_f_B(T_k), v)
        + C/dt*T_w*phi
        - dot(C*T_k*u_w, grad(phi))
        - dot(C*T_w*u_k, grad(phi))
        + K/Pr*dot(grad(T_w), grad(phi))
        + 1./dt*L*T_w*dP(T_k)*phi
        )*fenics.dx

    # Set the functional metric for the error estimator for adaptive mesh refinement.
    """I haven't found a good way to make this flexible yet.
    Ideally the user would be able to write the metric, but this would require giving the user
    access to much data that phaseflow is currently hiding.
    """
    M = P(T_k)*fenics.dx

    if adaptive_metric == 'phase_only':

        pass

    elif adaptive_metric == 'all':

        M += T_k*fenics.dx

        for i in range(dimensionality):

            M += u_k[i]*fenics.dx

    else:

        assert(False)

    # Make the problem.
    problem = fenics.NonlinearVariationalProblem(F, w_k, bcs, JF)

    # Make the solvers.
    """ For the purposes of this project, it would be better to just always use the adaptive solver; but
    unfortunately the adaptive solver encounters nan's whenever evaluating the error for problems not
    involving phase-change. So far my attempts at writing a MWE to reproduce the  issue have failed.
    """
    adaptive_solver = fenics.AdaptiveNonlinearVariationalSolver(problem, M)

    adaptive_solver.parameters['nonlinear_variational_solver']['newton_solver']['maximum_iterations']\
        = nlp_max_iterations

    adaptive_solver.parameters['nonlinear_variational_solver']['newton_solver']['absolute_tolerance']\
        = nlp_absolute_tolerance

    adaptive_solver.parameters['nonlinear_variational_solver']['newton_solver']['relative_tolerance']\
        = nlp_relative_tolerance

    static_solver = fenics.NonlinearVariationalSolver(problem)

    static_solver.parameters['newton_solver']['maximum_iterations'] = nlp_max_iterations

    static_solver.parameters['newton_solver']['absolute_tolerance'] = nlp_absolute_tolerance

    static_solver.parameters['newton_solver']['relative_tolerance'] = nlp_relative_tolerance

    # Open a context manager for the output file.
    with fenics.XDMFFile(output_dir + '/solution.xdmf') as solution_file:

        # Write the initial values.
        output.write_solution(solution_file, w_n, time)

        if start_time >= end_time - TIME_EPS:

            helpers.print_once("Start time is already too close to end time. Only writing initial values.")

            fe_field_interpolant = fenics.interpolate(w_n.leaf_node(), W)

            return fe_field_interpolant, mesh

        # Solve each time step.
        progress = fenics.Progress('Time-stepping')

        fenics.set_log_level(fenics.PROGRESS)

        while time < end_time - TIME_EPS:

            if adaptive:

                adaptive_solver.solve(adaptive_solver_tolerance)

                adaptive_solver.summary()

            else:

                static_solver.solve()

            time += time_step_size

            helpers.print_once("Reached time t = " + str(time))

            output.write_solution(solution_file, w_k, time)

            # Write checkpoint/restart files.
            restart_filepath = output_dir + '/restart_t' + str(time) + '.h5'

            with fenics.HDF5File(fenics.mpi_comm_world(), restart_filepath, 'w') as h5:

                h5.write(mesh.leaf_node(), 'mesh')

                h5.write(w_k.leaf_node(), 'w')

            if fenics.MPI.rank(fenics.mpi_comm_world()) is 0:

                with h5py.File(restart_filepath, 'r+') as h5:

                    h5.create_dataset('t', data=time)

            # Check for steady state.
            if stop_when_steady and steady(W, w_k, w_n, steady_relative_tolerance):

                helpers.print_once("Reached steady state at time t = " + str(time))

                break

            # Set initial values for next time step.
            w_n.leaf_node().vector()[:] = w_k.leaf_node().vector()

            # Report progress.
            progress.update(time / end_time)

            if time >= (end_time - fenics.dolfin.DOLFIN_EPS):

                helpers.print_once("Reached end time, t = " + str(end_time))

                break

    # Return the interpolant to sample inside of Python.
    w_n.rename('w', "state")

    fe_field_interpolant = fenics.interpolate(w_n.leaf_node(), W)

    return fe_field_interpolant, mesh

if __name__=='__main__':

    run()
<commit_msg>Stop when steady by default<commit_after>"""This module contains the core functionalty of Phaseflow."""
import fenics
import h5py
import numpy
import helpers
import globals
import default
import form
import output

TIME_EPS = 1.e-8

def make_mixed_fe(cell, pressure_degree=default.pressure_degree,
        temperature_degree=default.temperature_degree):
    """ Define the mixed finite element.
    MixedFunctionSpace used to be available but is now deprecated.
    To create the mixed space, I'm using the approach from https://fenicsproject.org/qa/11983/mixedfunctionspace-in-2016-2-0
    """
    velocity_degree = pressure_degree + 1

    velocity_element = fenics.VectorElement('P', cell, velocity_degree)

    pressure_element = fenics.FiniteElement('P', cell, pressure_degree)

    temperature_element = fenics.FiniteElement('P', cell, temperature_degree)

    mixed_element = fenics.MixedElement([velocity_element, pressure_element, temperature_element])

    return mixed_element

def steady(W, w, w_n, steady_relative_tolerance):
    """Check if solution has reached an approximately steady state."""
    steady = False

    time_residual = fenics.Function(W)

    time_residual.assign(w - w_n)

    unsteadiness = fenics.norm(time_residual, 'L2')/fenics.norm(w_n, 'L2')

    helpers.print_once(
        "Unsteadiness (L2 norm of relative time residual), || w_{n+1} || / || w_n || = "+str(unsteadiness))

    if (unsteadiness < steady_relative_tolerance):

        steady = True

    return steady

def run(output_dir = 'output/wang2010_natural_convection_air',
        rayleigh_number = default.parameters['Ra'],
        prandtl_number = default.parameters['Pr'],
        stefan_number = default.parameters['Ste'],
        heat_capacity = default.parameters['C'],
        thermal_conductivity = default.parameters['K'],
        liquid_viscosity = default.parameters['mu_l'],
        solid_viscosity = default.parameters['mu_s'],
        gravity = default.parameters['g'],
        m_B = default.m_B,
        ddT_m_B = default.ddT_m_B,
        penalty_parameter = 1.e-7,
        regularization = default.regularization,
        mesh=default.mesh,
        initial_values_expression = ("0.", "0.", "0.", "0.5*near(x[0],  0.) -0.5*near(x[0],  1.)"),
        boundary_conditions = [{'subspace': 0,
                'value_expression': ("0.", "0."), 'degree': 3,
                'location_expression': "near(x[0],  0.) | near(x[0],  1.) | near(x[1], 0.) | near(x[1],  1.)", 'method': 'topological'},
            {'subspace': 2,
                'value_expression': "0.5", 'degree': 2,
                'location_expression': "near(x[0],  0.)", 'method': 'topological'},
             {'subspace': 2,
                'value_expression': "-0.5", 'degree': 2,
                'location_expression': "near(x[0],  1.)", 'method': 'topological'}],
        start_time = 0.,
        end_time = 10.,
        time_step_size = 1.e-3,
        stop_when_steady = True,
        steady_relative_tolerance=1.e-4,
        adaptive = False,
        adaptive_metric = 'all',
        adaptive_solver_tolerance = 1.e-4,
        nlp_absolute_tolerance = 1.e-8,
        nlp_relative_tolerance = 1.e-8,
        nlp_max_iterations = 50,
        pressure_degree = default.pressure_degree,
        temperature_degree = default.temperature_degree,
        restart = False,
        restart_filepath = ''):
    """Run Phaseflow.

    Phaseflow is configured entirely through the arguments in this run() function.

    See the tests and examples for demonstrations of how to use this.
    """

    # Report inputs.
    helpers.print_once("Running Phaseflow with the following arguments:")

    helpers.print_once(helpers.arguments())

    helpers.mkdir_p(output_dir)

    if fenics.MPI.rank(fenics.mpi_comm_world()) is 0:

        arguments_file = open(output_dir + '/arguments.txt', 'w')

        arguments_file.write(str(helpers.arguments()))

        arguments_file.close()

    # Check if 1D/2D/3D.
    dimensionality = mesh.type().dim()

    helpers.print_once("Running "+str(dimensionality)+"D problem")

    # Initialize time.
    if restart:

        with h5py.File(restart_filepath, 'r') as h5:

            time = h5['t'].value

            assert(abs(time - start_time) < TIME_EPS)

    else:

        time = start_time

    # Define the mixed finite element and the solution function space.
    W_ele = make_mixed_fe(mesh.ufl_cell())

    W = fenics.FunctionSpace(mesh, W_ele)

    # Set the initial values.
    if restart:

        mesh = fenics.Mesh()

        with fenics.HDF5File(mesh.mpi_comm(), restart_filepath, 'r') as h5:

            h5.read(mesh, 'mesh', True)

        W_ele = make_mixed_fe(mesh.ufl_cell())

        W = fenics.FunctionSpace(mesh, W_ele)

        w_n = fenics.Function(W)

        with fenics.HDF5File(mesh.mpi_comm(), restart_filepath, 'r') as h5:

            h5.read(w_n, 'w')

    else:

        w_n = fenics.interpolate(fenics.Expression(initial_values_expression,
            element=W_ele), W)

    # Organize the boundary conditions.
    bcs = []

    for item in boundary_conditions:

        bcs.append(fenics.DirichletBC(W.sub(item['subspace']), item['value_expression'],
            item['location_expression'], method=item['method']))

    # Set the variational form.
    """Set local names for math operators to improve readability."""
    inner, dot, grad, div, sym = fenics.inner, fenics.dot, fenics.grad, fenics.div, fenics.sym

    """The linear, bilinear, and trilinear forms b, a, and c, follow the common notation
    for applying the finite element method to the incompressible Navier-Stokes equations,
    e.g. from danaila2014newton and huerta2003fefluids.
    """
    b = lambda u, q : -div(u)*q  # Divergence

    D = lambda u : sym(grad(u))  # Symmetric part of velocity gradient

    a = lambda mu, u, v : 2.*mu*inner(D(u), D(v))  # Stokes stress-strain

    c = lambda w, z, v : dot(dot(grad(z), w), v)  # Convection of the velocity field

    dt = fenics.Constant(time_step_size)

    Ra = fenics.Constant(rayleigh_number),

    Pr = fenics.Constant(prandtl_number)

    Ste = fenics.Constant(stefan_number)

    C = fenics.Constant(heat_capacity)

    K = fenics.Constant(thermal_conductivity)

    g = fenics.Constant(gravity)

    f_B = lambda T : m_B(T)*g  # Buoyancy force, $f = ma$

    gamma = fenics.Constant(penalty_parameter)

    T_f = fenics.Constant(regularization['T_f'])  # Center of regularized phase-field.

    r = fenics.Constant(regularization['r'])  # Regularization smoothing parameter.

    P = lambda T: 0.5*(1. - fenics.tanh(2.*(T_f - T)/r))  # Regularized phase field.

    mu_l = fenics.Constant(liquid_viscosity)

    mu_s = fenics.Constant(solid_viscosity)

    mu = lambda (T) : mu_s + (mu_l - mu_s)*P(T) # Variable viscosity.

    L = C/Ste  # Latent heat

    u_n, p_n, T_n = fenics.split(w_n)

    w_w = fenics.TrialFunction(W)

    u_w, p_w, T_w = fenics.split(w_w)

    v, q, phi = fenics.TestFunctions(W)

    w_k = fenics.Function(W)

    u_k, p_k, T_k = fenics.split(w_k)

    F = (
        b(u_k, q) - gamma*p_k*q
        + dot(u_k - u_n, v)/dt
        + c(u_k, u_k, v) + b(v, p_k) + a(mu(T_k), u_k, v)
        + dot(f_B(T_k), v)
        + C/dt*(T_k - T_n)*phi
        - dot(C*T_k*u_k, grad(phi))
        + K/Pr*dot(grad(T_k), grad(phi))
        + 1./dt*L*(P(T_k) - P(T_n))*phi
        )*fenics.dx

    ddT_f_B = lambda T : ddT_m_B(T)*g

    sech = lambda theta: 1./fenics.cosh(theta)

    dP = lambda T: sech(2.*(T_f - T)/r)**2/r

    dmu = lambda T : (mu_l - mu_s)*dP(T)

    # Set the Jacobian (formally the Gateaux derivative) in variational form.
    JF = (
        b(u_w, q) - gamma*p_w*q
        + dot(u_w, v)/dt
        + c(u_k, u_w, v) + c(u_w, u_k, v) + b(v, p_w)
        + a(T_w*dmu(T_k), u_k, v) + a(mu(T_k), u_w, v)
        + dot(T_w*ddT_f_B(T_k), v)
        + C/dt*T_w*phi
        - dot(C*T_k*u_w, grad(phi))
        - dot(C*T_w*u_k, grad(phi))
        + K/Pr*dot(grad(T_w), grad(phi))
        + 1./dt*L*T_w*dP(T_k)*phi
        )*fenics.dx

    # Set the functional metric for the error estimator for adaptive mesh refinement.
    """I haven't found a good way to make this flexible yet.
    Ideally the user would be able to write the metric, but this would require giving the user
    access to much data that phaseflow is currently hiding.
    """
    M = P(T_k)*fenics.dx

    if adaptive_metric == 'phase_only':

        pass

    elif adaptive_metric == 'all':

        M += T_k*fenics.dx

        for i in range(dimensionality):

            M += u_k[i]*fenics.dx

    else:

        assert(False)

    # Make the problem.
    problem = fenics.NonlinearVariationalProblem(F, w_k, bcs, JF)

    # Make the solvers.
    """ For the purposes of this project, it would be better to just always use the adaptive solver; but
    unfortunately the adaptive solver encounters nan's whenever evaluating the error for problems not
    involving phase-change. So far my attempts at writing a MWE to reproduce the  issue have failed.
    """
    adaptive_solver = fenics.AdaptiveNonlinearVariationalSolver(problem, M)

    adaptive_solver.parameters['nonlinear_variational_solver']['newton_solver']['maximum_iterations']\
        = nlp_max_iterations

    adaptive_solver.parameters['nonlinear_variational_solver']['newton_solver']['absolute_tolerance']\
        = nlp_absolute_tolerance

    adaptive_solver.parameters['nonlinear_variational_solver']['newton_solver']['relative_tolerance']\
        = nlp_relative_tolerance

    static_solver = fenics.NonlinearVariationalSolver(problem)

    static_solver.parameters['newton_solver']['maximum_iterations'] = nlp_max_iterations

    static_solver.parameters['newton_solver']['absolute_tolerance'] = nlp_absolute_tolerance

    static_solver.parameters['newton_solver']['relative_tolerance'] = nlp_relative_tolerance

    # Open a context manager for the output file.
    with fenics.XDMFFile(output_dir + '/solution.xdmf') as solution_file:

        # Write the initial values.
        output.write_solution(solution_file, w_n, time)

        if start_time >= end_time - TIME_EPS:

            helpers.print_once("Start time is already too close to end time. Only writing initial values.")

            fe_field_interpolant = fenics.interpolate(w_n.leaf_node(), W)

            return fe_field_interpolant, mesh

        # Solve each time step.
        progress = fenics.Progress('Time-stepping')

        fenics.set_log_level(fenics.PROGRESS)

        while time < end_time - TIME_EPS:

            if adaptive:

                adaptive_solver.solve(adaptive_solver_tolerance)

                adaptive_solver.summary()

            else:

                static_solver.solve()

            time += time_step_size

            helpers.print_once("Reached time t = " + str(time))

            output.write_solution(solution_file, w_k, time)

            # Write checkpoint/restart files.
            restart_filepath = output_dir + '/restart_t' + str(time) + '.h5'

            with fenics.HDF5File(fenics.mpi_comm_world(), restart_filepath, 'w') as h5:

                h5.write(mesh.leaf_node(), 'mesh')

                h5.write(w_k.leaf_node(), 'w')

            if fenics.MPI.rank(fenics.mpi_comm_world()) is 0:

                with h5py.File(restart_filepath, 'r+') as h5:

                    h5.create_dataset('t', data=time)

            # Check for steady state.
            if stop_when_steady and steady(W, w_k, w_n, steady_relative_tolerance):

                helpers.print_once("Reached steady state at time t = " + str(time))

                break

            # Set initial values for next time step.
            w_n.leaf_node().vector()[:] = w_k.leaf_node().vector()

            # Report progress.
            progress.update(time / end_time)

            if time >= (end_time - fenics.dolfin.DOLFIN_EPS):

                helpers.print_once("Reached end time, t = " + str(end_time))

                break

    # Return the interpolant to sample inside of Python.
    w_n.rename('w', "state")

    fe_field_interpolant = fenics.interpolate(w_n.leaf_node(), W)

    return fe_field_interpolant, mesh

if __name__=='__main__':

    run()
<|endoftext|>


<commit_before># Copyright 2011 James McCauley
#
# This file is part of POX.
#
# POX 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.
#
# POX 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 POX.  If not, see <http://www.gnu.org/licenses/>.

"""
The Topology module is the root of an object model composed of entities
like switches, hosts, links, etc.  This object model is populated by other
modules.  For example, openflow.topology populates the topology object
with OpenFlow switches.

Note that this means that you often want to invoke something like:
   $ ./pox.py topology openflow.discovery openflow.topology
"""

from pox.lib.revent import *
from pox.core import core
from pox.lib.addresses import *

class EntityEvent (Event):
  def __init__ (self, entity):
    Event.__init__(self)
    self.entity = entity

class EntityJoin (EntityEvent):
  """
  An entity has been added.

  Note that if there is a more specific join event defined for a particular
  entity, (e.g., SwitchJoin), this event will not be fired.

  TODO: or we could always raise EntityJoins along with SwitchJoins, which
  seems more intuitive to me.
  """
  pass

class EntityLeave (EntityEvent):
  """
  An entity has been removed

  Note that if there is a more specific leave event defined for a particular
  entity, (e.g., SwitchLeave), this event will not be fired.

  TODO: or we could always raise EntityLeaves along with SwitchLeaves, which
  seems more intuitive to me.
  """
  pass

class SwitchEvent (EntityEvent): pass

class SwitchJoin (SwitchEvent):
  """
  As opposed to ConnectionUp, SwitchJoin occurs over large time scales
  (e.g. an administrator physically moving a switch).
  """
  def __init__ (self, switch):
    Event.__init__(self)
    self.switch = switch

class SwitchLeave (SwitchEvent):
  """
  As opposed to ConnectionDown, SwitchLeave occurs over large time scales
  (e.g. an administrator physically moving a switch).
  """
  pass

class SwitchConnectionUp(SwitchEvent):
  def __init__(self, switch, connection):
    SwitchEvent.__init__(self, switch)
    self.switch = switch
    self.connection = connection

class SwitchConnectionDown(SwitchEvent): pass

class HostEvent (EntityEvent): pass
class HostJoin (HostEvent): pass
class HostLeave (HostEvent): pass

class Update (Event):
  """
  Fired by Topology whenever anything has changed
  """
  def __init__ (self, event):
    Event.__init__(self)
    self.event = event

class Entity (object):
  """
  Note that the Entity class is intentionally simple; It only serves as a
  convenient SuperClass type.

  It's up to subclasses to implement specific functionality (e.g.
  OpenFlow1.0 switch functionality).  The purpose of this design decision
  is to prevent protocol specific details from being leaked into this
  module... but this design decision does /not/ imply that pox.toplogy
  serves to define a generic interface to abstract entity types.

  NOTE: /all/ subclasses must call this superconstructor, since
        the unique self.id is field is used by Topology
  """
  # This is a counter used so that we can get unique IDs for entities.
  # Some entities don't need this because they have more meaningful
  # identifiers.
  _next_id = 1
  _all_ids = set()

  def __init__ (self, id=None):
    if id and id in Entity._all_ids:
      raise Exception("ID %s already taken" % str(id))
    else:
      while Entity._next_id in Entity._all_ids:
        Entity._next_id += 1
      id = Entity._next_id

    Entity._all_ids.add(id)
    self.id = id

class Host (Entity):
  """
  A generic Host entity.
  """
  def __init__(self):
    Entity.__init__(self)

class Switch (Entity):
  """
  Subclassed by protocol-specific switch classes,
  e.g. pox.openflow.topology.OpenFlowSwitch
  """
  def __init__(self, id=None):
    # Switches often have something more meaningful to use as an ID
    # (e.g., a DPID or MAC address), so they take it as a parameter.
    Entity.__init__(self, id)

class Port (Entity):
  def __init__ (self, num, hwAddr, name):
    Entity.__init__(self)
    self.number = num
    self.hwAddr = EthAddr(hwAddr)
    self.name = name

class Topology (EventMixin):
  _eventMixin_events = [
    SwitchJoin,
    SwitchLeave,
    HostJoin,
    HostLeave,
    EntityJoin,
    EntityLeave,

    Update
  ]

  _core_name = "topology" # We want to be core.topology

  def __init__ (self, name="topology"):
    EventMixin.__init__(self)
    self._entities = {}
    self.log = core.getLogger(name)

    # If a client registers a handler for these events after they have
    # already occurred, we promise to re-issue them to the newly joined
    # client.
    self._event_promises = {
      SwitchJoin : self._fulfill_SwitchJoin_promise
    }

  def getEntityByID (self, ID, fail=False):
    """
    Raises an exception if fail is True and the entity doesn't exist
    See also: The 'entity' property.
    """
    if fail:
      return self._entities[ID]
    else:
      return self._entities.get(ID, None)

  def removeEntity (self, entity):
    del self._entities[entity.id]
    self.log.info(str(entity) + " left")
    if isinstance(entity, Switch):
      self.raiseEvent(SwitchLeave, entity)
    elif isinstance(entity, Host):
      self.raiseEvent(HostLeave, entity)
    else:
      self.raiseEvent(EntityLeave, entity)

  def addEntity (self, entity):
    """ Will raise an exception if entity.id already exists """
    if entity.id in self._entities:
      raise RuntimeError("Entity exists")
    self._entities[entity.id] = entity
    self.log.info(str(entity) + " joined")
    if isinstance(entity, Switch):
      self.raiseEvent(SwitchJoin, entity)
    elif isinstance(entity, Host):
      self.raiseEvent(HostJoin, entity)
    else:
      self.raiseEvent(EntityJoin, entity)

  def getEntitiesOfType (self, t=Entity, subtypes=True):
    if subtypes is False:
      return [x for x in self._entities.itervalues() if type(x) is t]
    else:
      return [x for x in self._entities.itervalues() if isinstance(x, t)]

  def addListener(self, eventType, handler, once=False, weak=False,
                  priority=None, byName=False):
    """
    We interpose on EventMixin.addListener to check if the eventType is
    in our promise list. If so, trigger the handler for all previously
    triggered events.
    """
    if eventType in self._event_promises:
      self._event_promises[eventType](handler)

    return EventMixin.addListener(self, eventType, handler, once=once,
                                  weak=weak, priority=priority,
                                  byName=byName)

  def raiseEvent (self, event, *args, **kw):
    """
    Whenever we raise any event, we also raise an Update, so we extend
    the implementation in EventMixin.
    """
    rv = EventMixin.raiseEvent(self, event, *args, **kw)
    if type(event) is not Update:
      EventMixin.raiseEvent(self, Update(event))
    return rv

  def _fulfill_SwitchJoin_promise(self, handler):
    """ Trigger the SwitchJoin handler for all pre-existing switches """
    for switch in self.getEntitiesOfType(Switch, True):
      handler(SwitchJoin(switch))

  def __len__(self):
    return len(self._entities)

  def __str__(self):
    # TODO: display me graphically
    strings = []
    strings.append("topology (%d total entities)" % len(self._entities))
    for id,entity in self._entities.iteritems():
      strings.append("%s %s" % (str(id), str(entity)))

    return '\n'.join(strings)

<commit_msg>topology.topology: Fix bug where prefered ID was not being assinged<commit_after># Copyright 2011 James McCauley
#
# This file is part of POX.
#
# POX 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.
#
# POX 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 POX.  If not, see <http://www.gnu.org/licenses/>.

"""
The Topology module is the root of an object model composed of entities
like switches, hosts, links, etc.  This object model is populated by other
modules.  For example, openflow.topology populates the topology object
with OpenFlow switches.

Note that this means that you often want to invoke something like:
   $ ./pox.py topology openflow.discovery openflow.topology
"""

from pox.lib.revent import *
from pox.core import core
from pox.lib.addresses import *

class EntityEvent (Event):
  def __init__ (self, entity):
    Event.__init__(self)
    self.entity = entity

class EntityJoin (EntityEvent):
  """
  An entity has been added.

  Note that if there is a more specific join event defined for a particular
  entity, (e.g., SwitchJoin), this event will not be fired.

  TODO: or we could always raise EntityJoins along with SwitchJoins, which
  seems more intuitive to me.
  """
  pass

class EntityLeave (EntityEvent):
  """
  An entity has been removed

  Note that if there is a more specific leave event defined for a particular
  entity, (e.g., SwitchLeave), this event will not be fired.

  TODO: or we could always raise EntityLeaves along with SwitchLeaves, which
  seems more intuitive to me.
  """
  pass

class SwitchEvent (EntityEvent): pass

class SwitchJoin (SwitchEvent):
  """
  As opposed to ConnectionUp, SwitchJoin occurs over large time scales
  (e.g. an administrator physically moving a switch).
  """
  def __init__ (self, switch):
    Event.__init__(self)
    self.switch = switch

class SwitchLeave (SwitchEvent):
  """
  As opposed to ConnectionDown, SwitchLeave occurs over large time scales
  (e.g. an administrator physically moving a switch).
  """
  pass

class SwitchConnectionUp(SwitchEvent):
  def __init__(self, switch, connection):
    SwitchEvent.__init__(self, switch)
    self.switch = switch
    self.connection = connection

class SwitchConnectionDown(SwitchEvent): pass

class HostEvent (EntityEvent): pass
class HostJoin (HostEvent): pass
class HostLeave (HostEvent): pass

class Update (Event):
  """
  Fired by Topology whenever anything has changed
  """
  def __init__ (self, event):
    Event.__init__(self)
    self.event = event

class Entity (object):
  """
  Note that the Entity class is intentionally simple; It only serves as a
  convenient SuperClass type.

  It's up to subclasses to implement specific functionality (e.g.
  OpenFlow1.0 switch functionality).  The purpose of this design decision
  is to prevent protocol specific details from being leaked into this
  module... but this design decision does /not/ imply that pox.toplogy
  serves to define a generic interface to abstract entity types.

  NOTE: /all/ subclasses must call this superconstructor, since
        the unique self.id is field is used by Topology
  """
  # This is a counter used so that we can get unique IDs for entities.
  # Some entities don't need this because they have more meaningful
  # identifiers.
  _next_id = 1
  _all_ids = set()

  def __init__ (self, id=None):
    if id:
      if id in Entity._all_ids:
        raise Exception("ID %s already taken" % str(id))
    else:
      while Entity._next_id in Entity._all_ids:
        Entity._next_id += 1
      id = Entity._next_id

    Entity._all_ids.add(id)
    self.id = id

class Host (Entity):
  """
  A generic Host entity.
  """
  def __init__(self):
    Entity.__init__(self)

class Switch (Entity):
  """
  Subclassed by protocol-specific switch classes,
  e.g. pox.openflow.topology.OpenFlowSwitch
  """
  def __init__(self, id=None):
    # Switches often have something more meaningful to use as an ID
    # (e.g., a DPID or MAC address), so they take it as a parameter.
    Entity.__init__(self, id)

class Port (Entity):
  def __init__ (self, num, hwAddr, name):
    Entity.__init__(self)
    self.number = num
    self.hwAddr = EthAddr(hwAddr)
    self.name = name

class Topology (EventMixin):
  _eventMixin_events = [
    SwitchJoin,
    SwitchLeave,
    HostJoin,
    HostLeave,
    EntityJoin,
    EntityLeave,

    Update
  ]

  _core_name = "topology" # We want to be core.topology

  def __init__ (self, name="topology"):
    EventMixin.__init__(self)
    self._entities = {}
    self.log = core.getLogger(name)

    # If a client registers a handler for these events after they have
    # already occurred, we promise to re-issue them to the newly joined
    # client.
    self._event_promises = {
      SwitchJoin : self._fulfill_SwitchJoin_promise
    }

  def getEntityByID (self, ID, fail=False):
    """
    Raises an exception if fail is True and the entity doesn't exist
    See also: The 'entity' property.
    """
    if fail:
      return self._entities[ID]
    else:
      return self._entities.get(ID, None)

  def removeEntity (self, entity):
    del self._entities[entity.id]
    self.log.info(str(entity) + " left")
    if isinstance(entity, Switch):
      self.raiseEvent(SwitchLeave, entity)
    elif isinstance(entity, Host):
      self.raiseEvent(HostLeave, entity)
    else:
      self.raiseEvent(EntityLeave, entity)

  def addEntity (self, entity):
    """ Will raise an exception if entity.id already exists """
    if entity.id in self._entities:
      raise RuntimeError("Entity exists")
    self._entities[entity.id] = entity
    self.log.info(str(entity) + " joined")
    if isinstance(entity, Switch):
      self.raiseEvent(SwitchJoin, entity)
    elif isinstance(entity, Host):
      self.raiseEvent(HostJoin, entity)
    else:
      self.raiseEvent(EntityJoin, entity)

  def getEntitiesOfType (self, t=Entity, subtypes=True):
    if subtypes is False:
      return [x for x in self._entities.itervalues() if type(x) is t]
    else:
      return [x for x in self._entities.itervalues() if isinstance(x, t)]

  def addListener(self, eventType, handler, once=False, weak=False,
                  priority=None, byName=False):
    """
    We interpose on EventMixin.addListener to check if the eventType is
    in our promise list. If so, trigger the handler for all previously
    triggered events.
    """
    if eventType in self._event_promises:
      self._event_promises[eventType](handler)

    return EventMixin.addListener(self, eventType, handler, once=once,
                                  weak=weak, priority=priority,
                                  byName=byName)

  def raiseEvent (self, event, *args, **kw):
    """
    Whenever we raise any event, we also raise an Update, so we extend
    the implementation in EventMixin.
    """
    rv = EventMixin.raiseEvent(self, event, *args, **kw)
    if type(event) is not Update:
      EventMixin.raiseEvent(self, Update(event))
    return rv

  def _fulfill_SwitchJoin_promise(self, handler):
    """ Trigger the SwitchJoin handler for all pre-existing switches """
    for switch in self.getEntitiesOfType(Switch, True):
      handler(SwitchJoin(switch))

  def __len__(self):
    return len(self._entities)

  def __str__(self):
    # TODO: display me graphically
    strings = []
    strings.append("topology (%d total entities)" % len(self._entities))
    for id,entity in self._entities.iteritems():
      strings.append("%s %s" % (str(id), str(entity)))

    return '\n'.join(strings)

<|endoftext|>


<commit_before>#!/usr/bin/env python
# plic - Pluggable IDL Compiler                                -*-mode:python-*-
# Copyright (C) 2008 Tim Janik
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""PLIC-Rapicorn - Rapicorn type generator for PLIC

More details at http://www.testbit.eu/.
"""
import Decls

def encode_int (int):
  assert int < 268435456
  chars = (128 + (int >> 21),
           128 + ((int >> 14) & 0x7f),
           128 + ((int >> 7) & 0x7f),
           128 + (int & 0x7f))
  return "%c%c%c%c" % chars
def encode_string (string):
  return encode_int (len (string)) + string

def strcquote (string):
  result = ''
  for c in string:
    oc = ord (c)
    if oc <= 31 or oc >= 127:
      result += '\\' + oct (oc)[-3:]
    elif c == '"':
      result += r'\"'
    else:
      result += c
  return '"' + result + '"'

class Generator:
  def aux_strings (self, auxlist):
    result = encode_int (len (auxlist))
    for ad in auxlist:
      result += encode_string (ad)
    return result
  def type_info (self, type_info, unfoldrefs = False):
    tp = type_info
    s = { Decls.NUM       : '___i',
          Decls.REAL      : '___f',
          Decls.STRING    : '___s',
          Decls.ENUM      : '___e',
          Decls.SEQUENCE  : '___q',
          Decls.RECORD    : '___r',
          Decls.INTERFACE : '___c',
          }[tp.storage]
    aux = []
    s += self.aux_strings (aux)
    if tp.storage == Decls.SEQUENCE:
      s += encode_string (tp.elements[0])
      s += self.type_info (tp.elements[1])
    elif tp.storage == Decls.RECORD:
      s += encode_int (len (tp.fields))
      for fl in tp.fields:
        s += encode_string (fl[0])
        s += self.type_info (fl[1])
    elif tp.storage == Decls.ENUM:
      s += encode_int (len (tp.options))
      for op in tp.options:
        s += encode_string (op[0]) # ident
        s += encode_string (op[1]) # label
        s += encode_string (op[2]) # blurb
    elif tp.storage == Decls.INTERFACE:
      tp.prerequisites = []
      s += encode_int (len (tp.prerequisites))
      for pr in prerequisites:
        s += encode_string (pr)
    return encode_int (len (s)) + s
  def type_string (self, type_info):
    s = 'Rpcrn001'      # magic
    s += encode_string (type_info.name)
    s += self.type_info (type_info, True)
    return s
  def generate (self, namespace):
    for tp in namespace.types():
      print "TYPE:", strcquote (self.type_string (tp))

def generate (namespace_list, *args):
  gg = Generator()
  for ns in namespace_list:
    gg.generate (ns)

# control module exports
__all__ = ['generate']
<commit_msg>Rapicorn.py: use human redable number encoding for small numbers<commit_after>#!/usr/bin/env python
# plic - Pluggable IDL Compiler                                -*-mode:python-*-
# Copyright (C) 2008 Tim Janik
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""PLIC-Rapicorn - Rapicorn type generator for PLIC

More details at http://www.testbit.eu/.
"""
import Decls

def encode_int (int):
  if int < 1000:
    return "#%03u" % int
  assert int < 268435456
  chars = (128 + (int >> 21),
           128 + ((int >> 14) & 0x7f),
           128 + ((int >> 7) & 0x7f),
           128 + (int & 0x7f))
  return "%c%c%c%c" % chars
def encode_string (string):
  return encode_int (len (string)) + string

def strcquote (string):
  result = ''
  for c in string:
    oc = ord (c)
    if oc <= 31 or oc >= 127:
      result += '\\' + oct (oc)[-3:]
    elif c == '"':
      result += r'\"'
    else:
      result += c
  return '"' + result + '"'

class Generator:
  def aux_strings (self, auxlist):
    result = encode_int (len (auxlist))
    for ad in auxlist:
      result += encode_string (ad)
    return result
  def type_info (self, type_info, unfoldrefs = False):
    tp = type_info
    s = { Decls.NUM       : '___i',
          Decls.REAL      : '___f',
          Decls.STRING    : '___s',
          Decls.ENUM      : '___e',
          Decls.SEQUENCE  : '___q',
          Decls.RECORD    : '___r',
          Decls.INTERFACE : '___c',
          }[tp.storage]
    aux = []
    s += self.aux_strings (aux)
    if tp.storage == Decls.SEQUENCE:
      s += encode_string (tp.elements[0])
      s += self.type_info (tp.elements[1])
    elif tp.storage == Decls.RECORD:
      s += encode_int (len (tp.fields))
      for fl in tp.fields:
        s += encode_string (fl[0])
        s += self.type_info (fl[1])
    elif tp.storage == Decls.ENUM:
      s += encode_int (len (tp.options))
      for op in tp.options:
        s += encode_string (op[0]) # ident
        s += encode_string (op[1]) # label
        s += encode_string (op[2]) # blurb
    elif tp.storage == Decls.INTERFACE:
      tp.prerequisites = []
      s += encode_int (len (tp.prerequisites))
      for pr in prerequisites:
        s += encode_string (pr)
    return encode_int (len (s)) + s
  def type_string (self, type_info):
    s = 'Rpcrn001'      # magic
    s += encode_string (type_info.name)
    s += self.type_info (type_info, True)
    return s
  def generate (self, namespace):
    for tp in namespace.types():
      print "TYPE:", strcquote (self.type_string (tp))

def generate (namespace_list, *args):
  gg = Generator()
  for ns in namespace_list:
    gg.generate (ns)

# control module exports
__all__ = ['generate']
<|endoftext|>


<commit_before>version = '1.1.21'
<commit_msg>1.1.22<commit_after>version = '1.1.22'
<|endoftext|>


<commit_before>from giotto import get_config
from giotto.programs import Program, Manifest
from giotto.views import GiottoView, BasicView, jinja_template
from giotto.control import Redirection

from .middleware import NotAuthenticatedOrRedirect, AuthenticationMiddleware, NotAuthenticatedOrDie, LogoutMiddleware
from .models import create_session, basic_register, User

def create_auth_manifest(**kwargs):
    """
    Creates a basic authentication manifest for logging in, logging out and
    registering new accounts.
    """
    class AuthProgram(Program):
        pre_input_middleware = [AuthenticationMiddleware]

    def register(username, password, password2):
        """
        Decorated version of basic_register with a callback added.
        """
        result = basic_register(username, password, password2)
        callback = kwargs.pop('post_register_callback', None)
        if callback:
            session = get_config('db_session')
            user = session.query(User).filter_by(username=username).first()
            callback(user)
        return result

    return Manifest({
        'login': [
            AuthProgram(
                """
                Prints out the HTML form for logging in.
                """,
                name="Login (form)",
                input_middleware=[NotAuthenticatedOrRedirect('/')],
                view=BasicView(
                    html=jinja_template('login.html'),
                ),
            ),
            AuthProgram(
                """
                Matches up the username/password against the database, and adds the auth cookies.
                """,
                name="Login (post)",
                input_middleware=[NotAuthenticatedOrDie],
                controllers=['http-post', 'cmd'],
                model=[create_session, {'username': 'mock_user', 'session_key': 'XXXXXXXXXXXXXXX'}],
                view=BasicView(
                    persist=lambda m: {'giotto_session': m['session_key']},
                    html=lambda m: Redirection('/'),
                ),
            ),
        ],
        'logout': AuthProgram(
            """
            Send the user here to log them out. Removes their cookies and deletes the auth session.
            """,
            name="Logout",
            view=BasicView(
                html=Redirection('/'),
            ),
            output_middleware=[LogoutMiddleware],
        ),
        'register': [
            AuthProgram(
                """
                This program returns the HTML page with the form for registering a new account.
                HTTP-get only.
                """,
                name="Register (form)",
                input_middleware=[NotAuthenticatedOrRedirect('/')],
                view=BasicView(
                    html=jinja_template('register.html'),
                ),
            ),
            AuthProgram(
                """
                When you POST the register form, this program handles creating the new user, then redirecting you to '/'
                """,
                name="Register (post)",
                controllers=['http-post'],
                model=[register],
                view=BasicView(
                    persist=lambda m: {'giotto_session': m['session_key']},
                    html=lambda m: Redirection('/'),
                ),
            ),
        ],
    })<commit_msg>subtle error that prevents users from signing up<commit_after>from giotto import get_config
from giotto.programs import Program, Manifest
from giotto.views import GiottoView, BasicView, jinja_template
from giotto.control import Redirection

from .middleware import NotAuthenticatedOrRedirect, AuthenticationMiddleware, NotAuthenticatedOrDie, LogoutMiddleware
from .models import create_session, basic_register, User

def create_auth_manifest(**kwargs):
    """
    Creates a basic authentication manifest for logging in, logging out and
    registering new accounts.
    """
    class AuthProgram(Program):
        pre_input_middleware = [AuthenticationMiddleware]

    def register(username, password, password2):
        """
        Decorated version of basic_register with a callback added.
        """
        result = basic_register(username, password, password2)
        callback = kwargs.get('post_register_callback', None)
        if callback:
            session = get_config('db_session')
            user = session.query(User).filter_by(username=username).first()
            callback(user)
        return result

    return Manifest({
        'login': [
            AuthProgram(
                """
                Prints out the HTML form for logging in.
                """,
                name="Login (form)",
                input_middleware=[NotAuthenticatedOrRedirect('/')],
                view=BasicView(
                    html=jinja_template('login.html'),
                ),
            ),
            AuthProgram(
                """
                Matches up the username/password against the database, and adds the auth cookies.
                """,
                name="Login (post)",
                input_middleware=[NotAuthenticatedOrDie],
                controllers=['http-post', 'cmd'],
                model=[create_session, {'username': 'mock_user', 'session_key': 'XXXXXXXXXXXXXXX'}],
                view=BasicView(
                    persist=lambda m: {'giotto_session': m['session_key']},
                    html=lambda m: Redirection('/'),
                ),
            ),
        ],
        'logout': AuthProgram(
            """
            Send the user here to log them out. Removes their cookies and deletes the auth session.
            """,
            name="Logout",
            view=BasicView(
                html=Redirection('/'),
            ),
            output_middleware=[LogoutMiddleware],
        ),
        'register': [
            AuthProgram(
                """
                This program returns the HTML page with the form for registering a new account.
                HTTP-get only.
                """,
                name="Register (form)",
                input_middleware=[NotAuthenticatedOrRedirect('/')],
                view=BasicView(
                    html=jinja_template('register.html'),
                ),
            ),
            AuthProgram(
                """
                When you POST the register form, this program handles creating the new user, then redirecting you to '/'
                """,
                name="Register (post)",
                controllers=['http-post'],
                model=[register],
                view=BasicView(
                    persist=lambda m: {'giotto_session': m['session_key']},
                    html=lambda m: Redirection('/'),
                ),
            ),
        ],
    })<|endoftext|>


<commit_before>"""Utility functions for plotting.

Author: Seth Axen
E-mail: seth.axen@gmail.com"""
from collections import deque

def rgb_to_hsv(rgb):
    """Convert RGB colors to HSV colors."""
    r, g, b = tuple(map(float, rgb))
    if any([r > 1, g > 1, b > 1]):
        r /= 255.
        g /= 255.
        b /= 255.
    mmax = max(r, g, b)
    mmin = min(r, g, b)
    c = mmax - mmin
    if (c == 0.):
        hp = 0.
    elif (mmax == r):
        hp = ((g - b) / c) % 6
    elif (mmax == g):
        hp = ((b - r) / c) + 2
    elif (mmax == b):
        hp = ((r - g) / c) + 4
    h = 60 * hp
    v = mmax
    if (c == 0):
        s = 0
    else:
        s = c / v
    return (h, s, v)

def hsv_to_rgb(hsv):
    """Convert HSV colors to RGB colors."""
    h, s, v = tuple(map(float, hsv))
    c = v * s
    m = v - c
    hp = h / 60.
    x = c * (1. - abs((hp % 2) - 1.))
    hp = int(hp)
    rgb = deque((c + m, x + m, m))
    if (hp % 2):
        rgb.reverse()
        rgb.rotate((hp - 3) / 2)
    else:
        rgb.rotate(hp / 2)
    return tuple(rgb)

def lighten_rgb(rgb, p=0.):
    """Lighten RGB colors by percentage p of total."""
    h, s, v = rgb_to_hsv(rgb)
    print(rgb)
    print((h, s, v))
    hsv = (h, s, min(1, v + p))
    print(hsv)
    return hsv_to_rgb(hsv)
<commit_msg>removed print statements<commit_after>"""Utility functions for plotting.

Author: Seth Axen
E-mail: seth.axen@gmail.com"""
from collections import deque

def rgb_to_hsv(rgb):
    """Convert RGB colors to HSV colors."""
    r, g, b = tuple(map(float, rgb))
    if any([r > 1, g > 1, b > 1]):
        r /= 255.
        g /= 255.
        b /= 255.
    mmax = max(r, g, b)
    mmin = min(r, g, b)
    c = mmax - mmin
    if (c == 0.):
        hp = 0.
    elif (mmax == r):
        hp = ((g - b) / c) % 6
    elif (mmax == g):
        hp = ((b - r) / c) + 2
    elif (mmax == b):
        hp = ((r - g) / c) + 4
    h = 60 * hp
    v = mmax
    if (c == 0):
        s = 0
    else:
        s = c / v
    return (h, s, v)

def hsv_to_rgb(hsv):
    """Convert HSV colors to RGB colors."""
    h, s, v = tuple(map(float, hsv))
    c = v * s
    m = v - c
    hp = h / 60.
    x = c * (1. - abs((hp % 2) - 1.))
    hp = int(hp)
    rgb = deque((c + m, x + m, m))
    if (hp % 2):
        rgb.reverse()
        rgb.rotate((hp - 3) / 2)
    else:
        rgb.rotate(hp / 2)
    return tuple(rgb)

def lighten_rgb(rgb, p=0.):
    """Lighten RGB colors by percentage p of total."""
    h, s, v = rgb_to_hsv(rgb)
    hsv = (h, s, min(1, v + p))
    return hsv_to_rgb(hsv)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-TODAY
#    Pexego Sistemas Informáticos (http://www.pexego.es) All Rights Reserved
#    $Jesús Ventosinos Mayor$
#    Copyright (c) 2014 Didotech srl (info at didotech.com)
#
#    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/>.
#
##############################################################################

{
    'name': 'Project task time control',
    'version': '3.1.5.5',
    'category': 'Project Management',
    "sequence": 30,
    'complexity': "easy",
    'description': """
        Manages tasks, allowing you  start or stop time counter from the tasks list view.
        When you stop  a task the total working time is stored on it.
    """,
    'author': 'Pexego',
    'website': 'http://www.pexego.es',
    'images': [],
    'depends': [
        'project',
        'project_gtd'
    ],
    'init_xml': [],
    'update_xml': [
        'project_task.xml',
        'wizard/task_time_control_confirm_wizard.xml',
        'security/ir.model.access.csv',
        'security/project_security.xml'
    ],
    'demo_xml': [],
    'installable': True,
    'auto_install': False,
    'application': True,
}
<commit_msg>[task_time_control] Helpdesk - Supporto #165<commit_after># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-TODAY
#    Pexego Sistemas Informáticos (http://www.pexego.es) All Rights Reserved
#    $Jesús Ventosinos Mayor$
#    Copyright (c) 2014 Didotech srl (info at didotech.com)
#
#    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/>.
#
##############################################################################

{
    'name': 'Project task time control',
    'version': '3.1.6.5',
    'category': 'Project Management',
    "sequence": 30,
    'complexity': "easy",
    'description': """
        Manages tasks, allowing you  start or stop time counter from the tasks list view.
        When you stop  a task the total working time is stored on it.
    """,
    'author': 'Pexego',
    'website': 'http://www.pexego.es',
    'images': [],
    'depends': [
        'project',
        'project_gtd'
    ],
    'init_xml': [],
    'update_xml': [
        'project_task.xml',
        'wizard/task_time_control_confirm_wizard.xml',
        'security/ir.model.access.csv',
        'security/project_security.xml'
    ],
    'demo_xml': [],
    'installable': True,
    'auto_install': False,
    'application': True,
}
<|endoftext|>