<commit_before># -*- coding: utf-8 -*-
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright (C) 2021 GEM Foundation
#
# OpenQuake 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.
#
# OpenQuake 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 OpenQuake. If not, see <http://www.gnu.org/licenses/>.

"""
Conditional spectrum calculator
"""
import logging
import operator
import numpy

from openquake.baselib import parallel, general
from openquake.hazardlib.contexts import read_cmakers
from openquake.calculators import base

U16 = numpy.uint16
U32 = numpy.uint32

def conditional_spectrum(dstore, slc, cmaker, monitor):
    """
    :param dstore:
        a DataStore instance
    :param slc:
        a slice of ruptures
    :param cmaker:
        a :class:`openquake.hazardlib.gsim.base.ContextMaker` instance
    :param monitor:
        monitor of the currently running job
    :returns:
        dictionary grp_id -> poes of shape (N, L, G)
    """
    with monitor('reading contexts', measuremem=True):
        dstore.open('r')
        ctxs = cmaker.read_ctxs(dstore, slc)
        N = len(dstore['sitecol/sids'])
    return {cmaker.grp_id: cmaker.get_pmap(ctxs).array(N)}

@base.calculators.add('conditional_spectrum')
class ConditionalSpectrumCalculator(base.HazardCalculator):
    """
    Conditional spectrum calculator, to be used for few sites only
    """
    precalc = 'classical'
    accept_precalc = ['classical', 'disaggregation']

    def pre_checks(self):
        """
        Check the number of sites and the absence of atomic groups
        """
        if self.N >= 32768:
            raise ValueError('You can disaggregate at max 32,768 sites')
        few = self.oqparam.max_sites_disagg
        if self.N > few:
            raise ValueError(
                'The number of sites is to disaggregate is %d, but you have '
                'max_sites_disagg=%d' % (self.N, few))
        if hasattr(self, 'csm'):
            for sg in self.csm.src_groups:
                if sg.atomic:
                    raise NotImplementedError(
                        'Atomic groups are not supported yet')
        elif self.datastore['source_info'].attrs['atomic']:
            raise NotImplementedError(
                'Atomic groups are not supported yet')

    def execute(self):
        """
        Compute the conditional spectrum
        """
        oq = self.oqparam
        self.full_lt = self.datastore['full_lt']
        self.trts = list(self.full_lt.gsim_lt.values)
        [self.poe] = oq.poes_disagg
        self.imts = list(oq.imtls)
        self.M = len(self.imts)
        dstore = (self.datastore.parent if self.datastore.parent
                  else self.datastore)
        totrups = len(dstore['rup/mag'])
        logging.info('Reading {:_d} ruptures'.format(totrups))
        rdt = [('grp_id', U16), ('nsites', U16), ('idx', U32)]
        rdata = numpy.zeros(totrups, rdt)
        rdata['idx'] = numpy.arange(totrups)
        rdata['grp_id'] = dstore['rup/grp_id'][:]
        rdata['nsites'] = [len(sids) for sids in dstore['rup/sids_']]
        totweight = rdata['nsites'].sum()
        trt_smrs = dstore['trt_smrs'][:]
        rlzs_by_gsim = self.full_lt.get_rlzs_by_gsim_list(trt_smrs)
        L = oq.imtls.size
        poes_shape = (sum(len(rbg) for rbg in rlzs_by_gsim), self.N, L)
        self.datastore.create_dset('poes', float, poes_shape)
        G = max(len(rbg) for rbg in rlzs_by_gsim)
        maxw = 2 * 1024**3 / (16 * G * self.M)  # at max 2 GB
        maxweight = min(
            numpy.ceil(totweight / (oq.concurrent_tasks or 1)), maxw)
        U = 0
        Ta = 0
        self.cmakers = read_cmakers(self.datastore)
        self.datastore.swmr_on()
        smap = parallel.Starmap(conditional_spectrum, h5=self.datastore.hdf5)
        # IMPORTANT!! we rely on the fact that the classical part
        # of the calculation stores the ruptures in chunks of constant
        # grp_id, therefore it is possible to build (start, stop) slices
        for block in general.block_splitter(rdata, maxweight,
                                            operator.itemgetter('nsites'),
                                            operator.itemgetter('grp_id')):
            Ta += 1
            grp_id = block[0]['grp_id']
            G = len(rlzs_by_gsim[grp_id])
            cmaker = self.cmakers[grp_id]
            U = max(U, block.weight)
            slc = slice(block[0]['idx'], block[-1]['idx'] + 1)
            smap.submit((dstore, slc, cmaker))
        results = smap.reduce(self.agg_result)
        return results

    def agg_result(self, acc, result):
        """
        Collect the results coming from compute_disagg into self.results.

        :param acc: dictionary grp_id -> NLG
        :param result: dictionary grp_id -> NLG
        """
        with self.monitor('aggregating results'):
            [(grp_id, poes)] = result.items()
            if grp_id not in acc:
                acc[grp_id] = poes
            else:
                acc[grp_id] = 1 - (1 - acc[grp_id]) * (1 - poes)
        return acc

    def post_execute(self, acc):
        for grp_id, poes in acc.items():
            poes = poes.transpose(2, 0, 1)  # NLG -> GNL
            for g, mat in enumerate(poes):
                self.datastore['poes'][self.cmakers[grp_id].start + g] = mat
<commit_msg>Added DO NOT USE warning to the conditional_spectrum calculator<commit_after># -*- coding: utf-8 -*-
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright (C) 2021 GEM Foundation
#
# OpenQuake 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.
#
# OpenQuake 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 OpenQuake. If not, see <http://www.gnu.org/licenses/>.

"""
Conditional spectrum calculator
"""
import logging
import operator
import numpy

from openquake.baselib import parallel, general
from openquake.hazardlib.contexts import read_cmakers
from openquake.calculators import base

U16 = numpy.uint16
U32 = numpy.uint32

def conditional_spectrum(dstore, slc, cmaker, monitor):
    """
    :param dstore:
        a DataStore instance
    :param slc:
        a slice of ruptures
    :param cmaker:
        a :class:`openquake.hazardlib.gsim.base.ContextMaker` instance
    :param monitor:
        monitor of the currently running job
    :returns:
        dictionary grp_id -> poes of shape (N, L, G)
    """
    with monitor('reading contexts', measuremem=True):
        dstore.open('r')
        ctxs = cmaker.read_ctxs(dstore, slc)
        N = len(dstore['sitecol/sids'])
    return {cmaker.grp_id: cmaker.get_pmap(ctxs).array(N)}

@base.calculators.add('conditional_spectrum')
class ConditionalSpectrumCalculator(base.HazardCalculator):
    """
    Conditional spectrum calculator, to be used for few sites only
    """
    precalc = 'classical'
    accept_precalc = ['classical', 'disaggregation']

    def pre_checks(self):
        """
        Check the number of sites and the absence of atomic groups
        """
        if self.N >= 32768:
            raise ValueError('You can disaggregate at max 32,768 sites')
        few = self.oqparam.max_sites_disagg
        if self.N > few:
            raise ValueError(
                'The number of sites is to disaggregate is %d, but you have '
                'max_sites_disagg=%d' % (self.N, few))
        if hasattr(self, 'csm'):
            for sg in self.csm.src_groups:
                if sg.atomic:
                    raise NotImplementedError(
                        'Atomic groups are not supported yet')
        elif self.datastore['source_info'].attrs['atomic']:
            raise NotImplementedError(
                'Atomic groups are not supported yet')

    def execute(self):
        """
        Compute the conditional spectrum
        """
        logging.warning('Initial NOT WORKING version of the calculator; '
                        'DO NOT USE!')
        oq = self.oqparam
        self.full_lt = self.datastore['full_lt']
        self.trts = list(self.full_lt.gsim_lt.values)
        [self.poe] = oq.poes_disagg
        self.imts = list(oq.imtls)
        self.M = len(self.imts)
        dstore = (self.datastore.parent if self.datastore.parent
                  else self.datastore)
        totrups = len(dstore['rup/mag'])
        logging.info('Reading {:_d} ruptures'.format(totrups))
        rdt = [('grp_id', U16), ('nsites', U16), ('idx', U32)]
        rdata = numpy.zeros(totrups, rdt)
        rdata['idx'] = numpy.arange(totrups)
        rdata['grp_id'] = dstore['rup/grp_id'][:]
        rdata['nsites'] = [len(sids) for sids in dstore['rup/sids_']]
        totweight = rdata['nsites'].sum()
        trt_smrs = dstore['trt_smrs'][:]
        rlzs_by_gsim = self.full_lt.get_rlzs_by_gsim_list(trt_smrs)
        L = oq.imtls.size
        poes_shape = (sum(len(rbg) for rbg in rlzs_by_gsim), self.N, L)
        self.datastore.create_dset('poes', float, poes_shape)
        G = max(len(rbg) for rbg in rlzs_by_gsim)
        maxw = 2 * 1024**3 / (16 * G * self.M)  # at max 2 GB
        maxweight = min(
            numpy.ceil(totweight / (oq.concurrent_tasks or 1)), maxw)
        U = 0
        Ta = 0
        self.cmakers = read_cmakers(self.datastore)
        self.datastore.swmr_on()
        smap = parallel.Starmap(conditional_spectrum, h5=self.datastore.hdf5)
        # IMPORTANT!! we rely on the fact that the classical part
        # of the calculation stores the ruptures in chunks of constant
        # grp_id, therefore it is possible to build (start, stop) slices
        for block in general.block_splitter(rdata, maxweight,
                                            operator.itemgetter('nsites'),
                                            operator.itemgetter('grp_id')):
            Ta += 1
            grp_id = block[0]['grp_id']
            G = len(rlzs_by_gsim[grp_id])
            cmaker = self.cmakers[grp_id]
            U = max(U, block.weight)
            slc = slice(block[0]['idx'], block[-1]['idx'] + 1)
            smap.submit((dstore, slc, cmaker))
        results = smap.reduce(self.agg_result)
        return results

    def agg_result(self, acc, result):
        """
        Collect the results coming from compute_disagg into self.results.

        :param acc: dictionary grp_id -> NLG
        :param result: dictionary grp_id -> NLG
        """
        with self.monitor('aggregating results'):
            [(grp_id, poes)] = result.items()
            if grp_id not in acc:
                acc[grp_id] = poes
            else:
                acc[grp_id] = 1 - (1 - acc[grp_id]) * (1 - poes)
        return acc

    def post_execute(self, acc):
        for grp_id, poes in acc.items():
            poes = poes.transpose(2, 0, 1)  # NLG -> GNL
            for g, mat in enumerate(poes):
                self.datastore['poes'][self.cmakers[grp_id].start + g] = mat
<|endoftext|>


<commit_before># The Hazard Library
# Copyright (C) 2012-2018 GEM Foundation
#
# 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/>.

import numpy as np
from openquake.hazardlib.geo import Point, Line
from openquake.hazardlib.geo.surface import SimpleFaultSurface
from openquake.hazardlib.site import Site, SiteCollection
from openquake.hazardlib.geo.surface import SimpleFaultSurface
from openquake.hazardlib.source.rupture import BaseRupture

class Dummy:

    @classmethod
    def get_site_collection(cls, nelements, **kwargs):
        """
        Returns a site collection.

        :param int nelements:
            Number of sites included in the site collection
        """
        sites = []
        for n in range(nelements):
            s = Site(Point(0, 0))
            for key in kwargs:
                if np.size(kwargs[key]) > 1:
                    setattr(s, key, kwargs[key][n])
                else:
                    setattr(s, key, kwargs[key])
            sites.append(s)
        return SiteCollection(sites)

    @classmethod
    def get_surface(self):
        """ """
        hyp = Line([Point(0, 0.5)])
        trc = Line([Point(0, 0), Point(0, 1)])
        sfc = SimpleFaultSurface.from_fault_data(fault_trace=trc,
                                                 upper_seismogenic_depth=0,
                                                 lower_seismogenic_depth=15,
                                                 dip=90.,
                                                 mesh_spacing=10.)
        return sfc, hyp

    @classmethod
    def get_rupture(self, **kwargs):
        """
        """
        # Parameters
        if 'mag' in kwargs:
            mag = kwargs['mag']
        else:
            mag = 6.0
        if 'rake' in kwargs:
            rake = kwargs['rake']
        else:
            rake = 0
        if 'trt' in kwargs:
            trt = kwargs['trt']
        else:
            trt = 0
        # Get surface
        sfc, hyp = self.get_surface()
        # Create rupture
        rup = BaseRupture(mag, rake, trt, hyp, sfc)
        # Set attributes
        return rup
<commit_msg>Removed unused import [skip CI]<commit_after># The Hazard Library
# Copyright (C) 2012-2018 GEM Foundation
#
# 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/>.

import numpy as np
from openquake.hazardlib.geo import Point, Line
from openquake.hazardlib.geo.surface import SimpleFaultSurface
from openquake.hazardlib.site import Site, SiteCollection
from openquake.hazardlib.source.rupture import BaseRupture

class Dummy:

    @classmethod
    def get_site_collection(cls, nelements, **kwargs):
        """
        Returns a site collection.

        :param int nelements:
            Number of sites included in the site collection
        """
        sites = []
        for n in range(nelements):
            s = Site(Point(0, 0))
            for key in kwargs:
                if np.size(kwargs[key]) > 1:
                    setattr(s, key, kwargs[key][n])
                else:
                    setattr(s, key, kwargs[key])
            sites.append(s)
        return SiteCollection(sites)

    @classmethod
    def get_surface(self):
        """ """
        hyp = Line([Point(0, 0.5)])
        trc = Line([Point(0, 0), Point(0, 1)])
        sfc = SimpleFaultSurface.from_fault_data(fault_trace=trc,
                                                 upper_seismogenic_depth=0,
                                                 lower_seismogenic_depth=15,
                                                 dip=90.,
                                                 mesh_spacing=10.)
        return sfc, hyp

    @classmethod
    def get_rupture(self, **kwargs):
        """
        """
        # Parameters
        if 'mag' in kwargs:
            mag = kwargs['mag']
        else:
            mag = 6.0
        if 'rake' in kwargs:
            rake = kwargs['rake']
        else:
            rake = 0
        if 'trt' in kwargs:
            trt = kwargs['trt']
        else:
            trt = 0
        # Get surface
        sfc, hyp = self.get_surface()
        # Create rupture
        rup = BaseRupture(mag, rake, trt, hyp, sfc)
        # Set attributes
        return rup
<|endoftext|>


<commit_before>#! /usr/bin/env python3
#
# In this script we solve the Cahn-Hiilliard equation, which models the
# unmixing of two phases under the effect of surface tension.

from nutils import mesh, function, solver, sample, export, cli, testing
from nutils.expression_v2 import Namespace
import numpy, treelog, itertools, enum, typing

class stab(enum.Enum):
  none = '0' # for educational purposes only
  linear = '.5 dc^2 (6 - 6 c^2 + 8 c dc - 3 dc^2) / epsilon^2'
  optimal = '.5 dc^2 (1 - dc^2 / 12) / epsilon^2'

def main(nelems:int, etype:str, btype:str, degree:int, epsilon:typing.Optional[float],
         contactangle:float, timestep:float, mtol:float, seed:int, circle:bool, stab:stab):
  '''
  Cahn-Hilliard equation on a unit square/circle.

  .. arguments::

     nelems [20]
       Number of elements along domain edge.
     etype [square]
       Type of elements (square/triangle/mixed).
     btype [std]
       Type of basis function (std/spline), with availability depending on the
       configured element type.
     degree [2]
       Polynomial degree.
     epsilon []
       Interface thickness; defaults to an automatic value based on the
       configured mesh density if left unspecified.
     contactangle [90]
       Wall contact angle in degrees.
     timestep [.01]
       Time step.
     mtol [.01]
       Threshold value for chemical potential peak to peak difference, used as
       a stop criterion.
     seed [0]
       Random seed for the initial condition.
     circle [no]
       Select circular domain as opposed to a unit square.
     stab [linear]
       Stabilization method (linear/optimal/none).
  '''

  mineps = 1./nelems
  if epsilon is None:
    treelog.info('setting epsilon={}'.format(mineps))
    epsilon = mineps
  elif epsilon < mineps:
    treelog.warning('epsilon under crititical threshold: {} < {}'.format(epsilon, mineps))

  domain, geom = mesh.unitsquare(nelems, etype)
  bezier = domain.sample('bezier', 5) # sample for plotting

  ns = Namespace()
  if not circle:
    ns.x = geom
  else:
    angle = (geom-.5) * (numpy.pi/2)
    ns.x = function.sin(angle) * function.cos(angle)[[1,0]] / numpy.sqrt(2)
  ns.define_for('x', gradient='∇', normal='n', jacobians=('dV', 'dS'))
  ns.epsilon = epsilon
  ns.ewall = .5 * numpy.cos(contactangle * numpy.pi / 180)
  ns.cbasis = ns.mbasis = domain.basis('std', degree=degree)
  ns.c = function.dotarg('c', ns.cbasis)
  ns.dc = ns.c - function.dotarg('c0', ns.cbasis)
  ns.m = function.dotarg('m', ns.mbasis)
  ns.F = '.5 (c^2 - 1)^2 / epsilon^2'
  ns.dF = stab.value
  ns.dt = timestep

  nrg_mix = domain.integral('F dV' @ ns, degree=7)
  nrg_iface = domain.integral('.5 ∇_k(c) ∇_k(c) dV' @ ns, degree=7)
  nrg_wall = domain.boundary.integral('(abs(ewall) + c ewall) dS' @ ns, degree=7)
  nrg = nrg_mix + nrg_iface + nrg_wall + domain.integral('(dF - m dc - .5 dt epsilon^2 ∇_k(m) ∇_k(m)) dV' @ ns, degree=7)

  numpy.random.seed(seed)
  state = dict(c=numpy.random.normal(0,.5,ns.cbasis.shape), m=numpy.random.normal(0,.5,ns.mbasis.shape)) # initial condition

  with treelog.iter.plain('timestep', itertools.count()) as steps:
   for istep in steps:

    E = sample.eval_integrals(nrg_mix, nrg_iface, nrg_wall, **state)
    treelog.user('energy: {0:.3f} ({1[0]:.0f}% mixture, {1[1]:.0f}% interface, {1[2]:.0f}% wall)'.format(sum(E), 100*numpy.array(E)/sum(E)))

    x, c, m = bezier.eval(['x_i', 'c', 'm'] @ ns, **state)
    export.triplot('phase.png', x, c, tri=bezier.tri, clim=(-1,1))
    export.triplot('chempot.png', x, m, tri=bezier.tri)

    if numpy.ptp(m) < mtol:
      break

    state['c0'] = state['c']
    state = solver.optimize(['c', 'm'], nrg, arguments=state, tol=1e-10)

  return state

# If the script is executed (as opposed to imported), :func:`nutils.cli.run`
# calls the main function with arguments provided from the command line.

if __name__ == '__main__':
  cli.run(main)

# Once a simulation is developed and tested, it is good practice to save a few
# strategic return values for regression testing. The :mod:`nutils.testing`
# module, which builds on the standard :mod:`unittest` framework, facilitates
# this by providing :func:`nutils.testing.TestCase.assertAlmostEqual64` for the
# embedding of desired results as compressed base64 data.

class test(testing.TestCase):

  @testing.requires('matplotlib')
  def test_initial(self):
    state = main(nelems=3, etype='square', btype='std', degree=2, epsilon=None, contactangle=90, timestep=1, mtol=float('inf'), seed=0, circle=False, stab=stab.linear)
    with self.subTest('concentration'): self.assertAlmostEqual64(state['c'], '''
      eNoBYgCd/xM3LjTtNYs3MDcUyt41uc14zjo0LzKzNm812jFhNNMzwDYgzbMzV8o0yCM1rzWeypE3Tcnx
      L07NzTa4NlMyETREyrPIGMxYMl82VDbjy1/M8clZyf3IRjday6XLmMl6NRnJMF4tqQ==''')
    with self.subTest('chemical-potential'): self.assertAlmostEqual64(state['m'], '''
      eNoBYgCd/w7NQModNFjLtckB0VA0rDCiM+zKBMzPygjMcMr4yJcy0MsUyXY0OcoxMFo19zE5Np/JMDTG
      yk7KGstQzFgwvMnDNXk0Msm+Njc3SjZizebJEjbPy1w25zLsNfQzSjURLRk3Qt4uBQ==''')

  @testing.requires('matplotlib')
  def test_square(self):
    state = main(nelems=3, etype='square', btype='std', degree=2, epsilon=None, contactangle=90, timestep=1, mtol=.1, seed=0, circle=False, stab=stab.linear)
    with self.subTest('concentration'): self.assertAlmostEqual64(state['c'], '''
      eNoBYgCd/5o2nDZANsQ1dTQO1QXQmTaaNjw2vDVVNOrPqM5mNmY2xDWwNPDRNMsZyys2KjYdNQoyh8v9
      yffJ1zXTNSA0Ks2JyorJicluNWQ1IDJXy+/JNck3yWQ1WjX0MVHL78k3yTnJbYgt7Q==''')
    with self.subTest('chemical-potential'): self.assertAlmostEqual64(state['m'], '''
      eNpVyU0KgCAABeGrK9SiQCiQCsrwB8GjzLF8uJNvNYzBsuP5yGKmWlhxXKMSmxzchPGceF4iVU55+Ck0
      masD28JDNQ==''')

  @testing.requires('matplotlib')
  def test_contactangle(self):
    state = main(nelems=3, etype='square', btype='std', degree=2, epsilon=None, contactangle=45, timestep=1, mtol=.1, seed=0, circle=False, stab=stab.linear)
    with self.subTest('concentration'): self.assertAlmostEqual64(state['c'], '''
      eNoBYgCd/zs2bjYbNqs19zNqzRXMazaZNkw25zVxNLXOncxDNnU28zUqNeIwTsvKyhc2TDaANdozSMwp
      yuXJojXiNYo05c/Oyp3Jb8n7NE81iDK2ywfKO8kayXI03jQjLyrLzskZyfrIkb8vTg==''')
    with self.subTest('chemical-potential'): self.assertAlmostEqual64(state['m'], '''
      eNoNzCEOggAYgNFLvbPguILeQRpWgyQPoAYCuBGhWHVzGshOLegMSvffS1/5EqmF2sUnTKJylbO3l6mZ
      pb2TZ5jLrDWObmGlUDroDWFjq43H3dcvaqdz9TCGP1tYLVU=''')

  @testing.requires('matplotlib')
  def test_mixedcircle(self):
    state = main(nelems=3, etype='mixed', btype='std', degree=2, epsilon=None, contactangle=90, timestep=1, mtol=.1, seed=0, circle=True, stab=stab.linear)
    with self.subTest('concentration'): self.assertAlmostEqual64(state['c'], '''
      eNoBYgCd/y415zQQNUw1dzVTNB01fTS4My81dDQpNWU03DJENDo0rjEVMCfMj8vjysUzozMmz3E0VCyg
      1I00MDMWM3bQU8t4ym7Kd8uKzoTPgc7PzmfOYs3ZzJvM6jIozunL7sqezgXMYsUuJg==''')
    with self.subTest('chemical-potential'): self.assertAlmostEqual64(state['m'], '''
      eNoljEkKgEAQxP7/B/EkiCCO27gOuJxy8FEGpSEUoaoTkYWTgyAjHRuTZpGzHGgpKchMMz2JRnN/l6j1
      uctge2W08W8fdlPF5ccXGqBI7Q==''')
<commit_msg>use function.eval in cahnhilliard example<commit_after>#! /usr/bin/env python3
#
# In this script we solve the Cahn-Hiilliard equation, which models the
# unmixing of two phases under the effect of surface tension.

from nutils import mesh, function, solver, export, cli, testing
from nutils.expression_v2 import Namespace
import numpy, treelog, itertools, enum, typing

class stab(enum.Enum):
  none = '0' # for educational purposes only
  linear = '.5 dc^2 (6 - 6 c^2 + 8 c dc - 3 dc^2) / epsilon^2'
  optimal = '.5 dc^2 (1 - dc^2 / 12) / epsilon^2'

def main(nelems:int, etype:str, btype:str, degree:int, epsilon:typing.Optional[float],
         contactangle:float, timestep:float, mtol:float, seed:int, circle:bool, stab:stab):
  '''
  Cahn-Hilliard equation on a unit square/circle.

  .. arguments::

     nelems [20]
       Number of elements along domain edge.
     etype [square]
       Type of elements (square/triangle/mixed).
     btype [std]
       Type of basis function (std/spline), with availability depending on the
       configured element type.
     degree [2]
       Polynomial degree.
     epsilon []
       Interface thickness; defaults to an automatic value based on the
       configured mesh density if left unspecified.
     contactangle [90]
       Wall contact angle in degrees.
     timestep [.01]
       Time step.
     mtol [.01]
       Threshold value for chemical potential peak to peak difference, used as
       a stop criterion.
     seed [0]
       Random seed for the initial condition.
     circle [no]
       Select circular domain as opposed to a unit square.
     stab [linear]
       Stabilization method (linear/optimal/none).
  '''

  mineps = 1./nelems
  if epsilon is None:
    treelog.info('setting epsilon={}'.format(mineps))
    epsilon = mineps
  elif epsilon < mineps:
    treelog.warning('epsilon under crititical threshold: {} < {}'.format(epsilon, mineps))

  domain, geom = mesh.unitsquare(nelems, etype)
  bezier = domain.sample('bezier', 5) # sample for plotting

  ns = Namespace()
  if not circle:
    ns.x = geom
  else:
    angle = (geom-.5) * (numpy.pi/2)
    ns.x = function.sin(angle) * function.cos(angle)[[1,0]] / numpy.sqrt(2)
  ns.define_for('x', gradient='∇', normal='n', jacobians=('dV', 'dS'))
  ns.epsilon = epsilon
  ns.ewall = .5 * numpy.cos(contactangle * numpy.pi / 180)
  ns.cbasis = ns.mbasis = domain.basis('std', degree=degree)
  ns.c = function.dotarg('c', ns.cbasis)
  ns.dc = ns.c - function.dotarg('c0', ns.cbasis)
  ns.m = function.dotarg('m', ns.mbasis)
  ns.F = '.5 (c^2 - 1)^2 / epsilon^2'
  ns.dF = stab.value
  ns.dt = timestep

  nrg_mix = domain.integral('F dV' @ ns, degree=7)
  nrg_iface = domain.integral('.5 ∇_k(c) ∇_k(c) dV' @ ns, degree=7)
  nrg_wall = domain.boundary.integral('(abs(ewall) + c ewall) dS' @ ns, degree=7)
  nrg = nrg_mix + nrg_iface + nrg_wall + domain.integral('(dF - m dc - .5 dt epsilon^2 ∇_k(m) ∇_k(m)) dV' @ ns, degree=7)

  numpy.random.seed(seed)
  state = dict(c=numpy.random.normal(0,.5,ns.cbasis.shape), m=numpy.random.normal(0,.5,ns.mbasis.shape)) # initial condition

  with treelog.iter.plain('timestep', itertools.count()) as steps:
   for istep in steps:

    E = function.eval([nrg_mix, nrg_iface, nrg_wall], **state)
    treelog.user('energy: {0:.3f} ({1[0]:.0f}% mixture, {1[1]:.0f}% interface, {1[2]:.0f}% wall)'.format(sum(E), 100*numpy.array(E)/sum(E)))

    x, c, m = bezier.eval(['x_i', 'c', 'm'] @ ns, **state)
    export.triplot('phase.png', x, c, tri=bezier.tri, clim=(-1,1))
    export.triplot('chempot.png', x, m, tri=bezier.tri)

    if numpy.ptp(m) < mtol:
      break

    state['c0'] = state['c']
    state = solver.optimize(['c', 'm'], nrg, arguments=state, tol=1e-10)

  return state

# If the script is executed (as opposed to imported), :func:`nutils.cli.run`
# calls the main function with arguments provided from the command line.

if __name__ == '__main__':
  cli.run(main)

# Once a simulation is developed and tested, it is good practice to save a few
# strategic return values for regression testing. The :mod:`nutils.testing`
# module, which builds on the standard :mod:`unittest` framework, facilitates
# this by providing :func:`nutils.testing.TestCase.assertAlmostEqual64` for the
# embedding of desired results as compressed base64 data.

class test(testing.TestCase):

  @testing.requires('matplotlib')
  def test_initial(self):
    state = main(nelems=3, etype='square', btype='std', degree=2, epsilon=None, contactangle=90, timestep=1, mtol=float('inf'), seed=0, circle=False, stab=stab.linear)
    with self.subTest('concentration'): self.assertAlmostEqual64(state['c'], '''
      eNoBYgCd/xM3LjTtNYs3MDcUyt41uc14zjo0LzKzNm812jFhNNMzwDYgzbMzV8o0yCM1rzWeypE3Tcnx
      L07NzTa4NlMyETREyrPIGMxYMl82VDbjy1/M8clZyf3IRjday6XLmMl6NRnJMF4tqQ==''')
    with self.subTest('chemical-potential'): self.assertAlmostEqual64(state['m'], '''
      eNoBYgCd/w7NQModNFjLtckB0VA0rDCiM+zKBMzPygjMcMr4yJcy0MsUyXY0OcoxMFo19zE5Np/JMDTG
      yk7KGstQzFgwvMnDNXk0Msm+Njc3SjZizebJEjbPy1w25zLsNfQzSjURLRk3Qt4uBQ==''')

  @testing.requires('matplotlib')
  def test_square(self):
    state = main(nelems=3, etype='square', btype='std', degree=2, epsilon=None, contactangle=90, timestep=1, mtol=.1, seed=0, circle=False, stab=stab.linear)
    with self.subTest('concentration'): self.assertAlmostEqual64(state['c'], '''
      eNoBYgCd/5o2nDZANsQ1dTQO1QXQmTaaNjw2vDVVNOrPqM5mNmY2xDWwNPDRNMsZyys2KjYdNQoyh8v9
      yffJ1zXTNSA0Ks2JyorJicluNWQ1IDJXy+/JNck3yWQ1WjX0MVHL78k3yTnJbYgt7Q==''')
    with self.subTest('chemical-potential'): self.assertAlmostEqual64(state['m'], '''
      eNpVyU0KgCAABeGrK9SiQCiQCsrwB8GjzLF8uJNvNYzBsuP5yGKmWlhxXKMSmxzchPGceF4iVU55+Ck0
      masD28JDNQ==''')

  @testing.requires('matplotlib')
  def test_contactangle(self):
    state = main(nelems=3, etype='square', btype='std', degree=2, epsilon=None, contactangle=45, timestep=1, mtol=.1, seed=0, circle=False, stab=stab.linear)
    with self.subTest('concentration'): self.assertAlmostEqual64(state['c'], '''
      eNoBYgCd/zs2bjYbNqs19zNqzRXMazaZNkw25zVxNLXOncxDNnU28zUqNeIwTsvKyhc2TDaANdozSMwp
      yuXJojXiNYo05c/Oyp3Jb8n7NE81iDK2ywfKO8kayXI03jQjLyrLzskZyfrIkb8vTg==''')
    with self.subTest('chemical-potential'): self.assertAlmostEqual64(state['m'], '''
      eNoNzCEOggAYgNFLvbPguILeQRpWgyQPoAYCuBGhWHVzGshOLegMSvffS1/5EqmF2sUnTKJylbO3l6mZ
      pb2TZ5jLrDWObmGlUDroDWFjq43H3dcvaqdz9TCGP1tYLVU=''')

  @testing.requires('matplotlib')
  def test_mixedcircle(self):
    state = main(nelems=3, etype='mixed', btype='std', degree=2, epsilon=None, contactangle=90, timestep=1, mtol=.1, seed=0, circle=True, stab=stab.linear)
    with self.subTest('concentration'): self.assertAlmostEqual64(state['c'], '''
      eNoBYgCd/y415zQQNUw1dzVTNB01fTS4My81dDQpNWU03DJENDo0rjEVMCfMj8vjysUzozMmz3E0VCyg
      1I00MDMWM3bQU8t4ym7Kd8uKzoTPgc7PzmfOYs3ZzJvM6jIozunL7sqezgXMYsUuJg==''')
    with self.subTest('chemical-potential'): self.assertAlmostEqual64(state['m'], '''
      eNoljEkKgEAQxP7/B/EkiCCO27gOuJxy8FEGpSEUoaoTkYWTgyAjHRuTZpGzHGgpKchMMz2JRnN/l6j1
      uctge2W08W8fdlPF5ccXGqBI7Q==''')
<|endoftext|>


<commit_before>r"""
This model calculates intensity using simple linear function
Definition
----------

The scattering intensity $I(q)$ is calculated as

.. math::

    I(q) = A + B*q

.. figure:: img/broad_peak_1d.jpg

    1D plot using the default values (w/200 data point).

References
----------

None.

"""

from numpy import vectorize
from numpy import inf

name = "line"
title = "Line model"
description = """\
      I(q) = A + B*q

      List of default parameters:
      A = intercept
      B = slope
      """
category = "shape-independent"

# pylint: disable=bad-whitespace, line-too-long
#             ["name", "units", default, [lower, upper], "type", "description"],
parameters = [["intercept",     "cm^-1",        1.0, [-inf, inf], "", "intercept in linear model"],
              ["slope",     "Ang*cm^-1",    1.0, [-inf, inf], "", "slope in linear model"],
              ]

def Iq(q, intercept, slope):
    """
    :param q:           Input q-value
    :param intercept:   Intrecept in linear model
    :param slope:       Slope in linear model
    :return:            Calculated Intensity
    """

    inten = intercept + slope*q
    return inten

Iq.vectorized = True # Iq accepts an array of q values

def Iqxy(qx, qy, *args):
    """
    :param qx:   Input q_x-value
    :param qy:   Input q_y-value
    :param args: Remaining arguments
    :return:     2D-Intensity
    """
    #TODO: Instrcution tels 2D has different deffinition than oher models
    #return Iq(sqrt(qx ** 2 + qy ** 2), *args)
    return  Iq(qx,*args)*Iq(qy,*args)

Iqxy.vectorized = True # Iqxy accepts an array of qx, qy values

demo = dict(scale=1, background=0.1,
            intercept=1.0, slope=1.0)

oldname = "LineModel"
oldpars = dict(intercept='A', slope='B', scale=None)<commit_msg>Added unit tests to line model<commit_after>r"""
This model calculates intensity using simple linear function
Definition
----------

The scattering intensity $I(q)$ is calculated as

.. math::

    I(q) = A + B*q

.. figure:: img/broad_peak_1d.jpg

    1D plot using the default values (w/200 data point).

References
----------

None.

"""
from numpy import inf

name = "line"
title = "Line model"
description = """\
      I(q) = A + B*q

      List of default parameters:
      A = intercept
      B = slope
      """
category = "shape-independent"

# pylint: disable=bad-whitespace, line-too-long
#             ["name", "units", default, [lower, upper], "type", "description"],
parameters = [["intercept",     "cm^-1",        1.0, [-inf, inf], "", "intercept in linear model"],
              ["slope",     "Ang*cm^-1",    1.0, [-inf, inf], "", "slope in linear model"],
              ]
# pylint: enable=bad-whitespace, line-too-long

def Iq(q, intercept, slope):
    """
    :param q:           Input q-value
    :param intercept:   Intrecept in linear model
    :param slope:       Slope in linear model
    :return:            Calculated Intensity
    """
    inten = intercept + slope*q
    return inten

Iq.vectorized = True # Iq accepts an array of q values

def Iqxy(qx, qy, *args):
    """
    :param qx:   Input q_x-value
    :param qy:   Input q_y-value
    :param args: Remaining arguments
    :return:     2D-Intensity
    """
    #TODO: Instrcution tels 2D has different deffinition than oher models
    #return Iq(sqrt(qx ** 2 + qy ** 2), *args)
    return  Iq(qx, *args)*Iq(qy, *args)

Iqxy.vectorized = True # Iqxy accepts an array of qx, qy values

demo = dict(intercept=1.0, slope=1.0)

oldname = "LineModel"
oldpars = dict(intercept='A', slope='B', scale=None, background=None)

tests = [
    # Accuracy tests based on content in test/utest_other_models.py
    [{'intercept':   1.0,
      'slope': 1.0,
     }, 0.4, 0.4],

    [{'intercept':   1.0,
      'slope': 1.0,
     }, 1.3, 1.3],

    [{'intercept':   1.0,
      'slope': 1.0,
     },0.5, 0.5],

    [{'intercept':   1.0,
      'slope': 1.0,
     }, 1.57, 1.57],
]<|endoftext|>


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

from collections import namedtuple
import tyr_ast as ast
from tyr_error import TyrSyntaxError

MatchPattern = namedtuple('Match', ['pattern', 'body'])
ConditionElif = namedtuple('ConditionElif', ['cond', 'body'])

class ParserError(TyrSyntaxError):
    def __init__(self, message, token):
        super(ParserError, self).__init__(token.lineno, token.column, message)

def enter_scope(parser, name):
    class State(object):
        def __enter__(self):
            parser.scope.append(name)

        def __exit__(self, exc_type, exc_val, exc_tb):
            parser.scope.pop()

    return State()

class Subparser(object):
    PRECEDENCE = {
        'call': 10,
        'subscript': 10,

        'unary': 9,

        '*': 7,
        '/': 7,
        '%': 7,

        '+': 6,
        '-': 6,

        '>': 5,
        '>= ': 5,
        '<': 5,
        '<=': 5,

        '==': 4,
        '!=': 4,

        '&&': 3,

        '||': 2,

        '..': 1,
        '...': 1,
    }

    def get_subparser(self, token, subparsers, default=None):
        cls = subparsers.get(token.name, default)
        if cls:
            return cls()

class PrefixSubparser(Subparser):
    def parse(self, parser, tokens):
        raise NotImplementedError()

class InfixSubparser(Subparser):
    def parse(self, parser, tokens, left):
        raise NotImplementedError()

    def get_precedence(self, token):
        raise NotImplementedError()

class NumberExpression(PrefixSubparser):
    """number_expr: NUMBER"""
    def parse(self, parser, tokens):
        token = tokens.consume_expected('NUMBER')
        return ast.Number(token.value)

class StringExpression(PrefixSubparser):
    """str_expr: STRING"""
    def parse(self, parser, tokens):
        token = tokens.consume_expected('STRING')
        return ast.String(token.value)

class NameExpression(PrefixSubparser):
    """name_expr: NAME"""
    def parse(self, parser, tokens):
        token = tokens.consume_expected('NAME')
        return ast.Identifier(token.value)

class UnaryOperatorExpression(PrefixSubparser):
    """prefix_expr: OPERATOR expr"""
    SUPPORTED_OPERATORS = ['-', '!']
    def parse(self, parser, tokens):
        token = tokens.consume_expected('OPERATOR')
        if token.value not in self.SUPPORTED_OPERATORS:
            raise ParserError('Unary operator {} is not supported'.format(token.value), token)
        right = Expression().parse(parser, tokens, self.get_precedence(token))
        if not right:
            raise ParserError('Excepted expression'.format(token.value), tokens.consume())
        return ast.UnaryOperator(token.value, right)

    def get_precedence(self, token):
        return self.PRECEDENCE['unary']

class GroupExpression(PrefixSubparser):
    """group_expr: LPAREN expr RPAREN"""
    def parse(self, parser, tokens):
        tokens.consume_expected('LPAREN')
        right = Expression().parse(parser, tokens)
        tokens.consume_expected('RPAREN')
        return right

class ArrayExpression(PrefixSubparser):
    """array_epxr: LBRACK list_of_expr? RBRACK"""
    def parse(self, parser, tokens):
        tokens.consume_expected('LBRACK')
        items = ListOfExpressions().parse(parser, tokens)
        tokens.consume_expected('RBRACK')
        return ast.Array(items)

class DictionaryExpression(PrefixSubparser):
    """dict_expr: LCBRACK (expr COLON expr COMMA)* RCBRACK"""
    def parse_keyvalues(self, parser, tokens):
        items = []
        while not tokens.is_end():
            key = Expression().parse(parser, tokens)
            if key:
                tokens.consume_expected('COLON')
                value = Expression().parse(parser, tokens)
                if value is None:
                    raise ParserError('Dictionary value excepted', tokens.consume())
                items.append((key, value))
            else:
                break
            if tokens.current().name == 'COMMA':
                tokens.consume_expected('COMMA')
            else:
                break
        return items

    def parse(self, parser, tokens):
        tokens.consume_expected('LCBRACK')
        items = self.parse_keyvalues(parser, tokens)
        tokens.consume_expected('RCBRACK')
        return ast.Dictionary(items)

class BinaryOperatorExpression(InfixSubparser):
    """infix_expr: expr OPERATOR expr"""
    def parse(self, parser, tokens, left):
        token = tokens.consume_expected('OPERATOR')
        right = Expression().parse(parser, tokens, self.get_precedence(token))
        if right is None:
            raise ParserError('Excepted expression'.format(token.value), tokens.consume())
        return ast.BinaryOperator(token.value, left, right)

    def get_precedence(self, token):
        return self.PRECEDENCE[token.value]

class CallExpression(InfixSubparser):
    """call_expr: NAME LPAREN list_of_expr? RPAREN"""
    def parse(self, parser, tokens, left):
        tokens.consume_expected('LPAREN')
        arguments = ListOfExpressions().parse(parser, tokens)
        tokens.consume_expected('RPAREN')
        return ast.Call(left, arguments)

    def get_precedence(self, token):
        return self.PRECEDENCE['call']

class SubscriptOperatorExpression(InfixSubparser):
    """subscript_expr: NAME LBRACK expr RBRACK"""
    def parse(self, parser, tokens, left):
        tokens.consume_expected('LBRACK')
        key = Expression().parse(parser, tokens)
        if key is None:
            raise ParserError('Subscript operator key is required', tokens.current())
        tokens.consume_expected('RBRACK')
        return ast.SubscriptOperator(left, key)

    def get_precedence(self, token):
        return self.PRECEDENCE['subscript']

class Expression(Subparser):
    """expr:
        number_expr | str_expr | name_expr | group_expr |
        array_epxr | dict_expr | prefix_expr | infix_expr |
        call_expr | subscript_expr
    """
    def get_prefix_subparser(self, token):
        return self.get_subparser(token, {
            'NUMBER': NumberExpression,
            'STRING': StringExpression,
            'NAME': NameExpression,
            'LPAREN': GroupExpression,
            'LBRACK': ArrayExpression,
            'LCBRACK': DictionaryExpression,
            'OPERATOR': UnaryOperatorExpression,
        })

    def get_infix_subparser(self, token):
        return self.get_subparser(token, {
            'OPERATOR': BinaryOperatorExpression,
            'LPAREN': CallExpression,
            'LBRACK': SubscriptOperatorExpression,
        })

    def get_next_precedence(self, tokens):
        if not tokens.is_end():
            token = token.current()
            parser = self.get_infix_subparser(token)
            if parser != None:
                return parser.get_precedence(token)
        return 0

    def parse(self, parser, tokens, precedence=0):
        subparser = self.get_prefix_subparser(tokens.current())
        if subparser != None:
            left = subparser.parse(parser, tokens)
            if left != None:
                while precedence < self.get_next_precedence(tokens):
                    op = self.get_infix_subparser(token.current()).parse(parser, tokens, left)
                    if op != None:
                        left = op
                return left

class ListOfExpressions(Subparser):
    """list_of_expr: (expr COMMA)*"""
    def parse(self, parser, tokens):
        items = []
        while not tokens.is_end():
            exp = Expression().parse(parser, tokens)
            if exp != None:
                items.append(exp)
            else:
                break
            if tokens.current().name == 'COMMA':
                tokens.consume_expected('COMMA')
            else:
                break
        return items

class Block(Subparser):
    """block: NEWLINE INDENT stmts DEDENT"""
    def parse(self, parser, tokens):
        tokens.consume_expected('NEWLINE', 'INDENT')
        statements = Statements().parse(parser, tokens)
        tokens.consume_expected('DEDENT')
        return statements

class FunctionStatement(Subparser):
    """func_stmt: FUNCTION NAME LPAREN func_params? RPAREN COLON block"""
    def parse_params(self, tokens):
        params = []
        if tokens.current().name == 'NAME':
            while not tokens.is_end():
                id_token = tokens.consume_expected('NAME')
                params.append(id_token.value)
                if tokens.current().name == 'COMMA':
                    tokens.consume_expected('COMMA')
                else:
                    break
        return params

    def parse(self, parser, tokens):
        tokens.consume_expected('FUNCTION')
        id_token = tokens.consume_expected('NAME')
        tokens.consume_expected('LPAREN')
        arguments = self.parse_params(tokens)
        tokens.consume_expected('RPAREN', 'COLON')
        with enter_scope(parser, 'function'):
            block = Block().parse(parser, tokens)
        if block is None:
            raise ParserError('Excepted function body', tokens.current())
        return ast.Function(id_token.value, arguments, block)

class ConditionStatement(Subparser):
    """cond_stmt:
        IF exp COLON block (ELIF exp COLON block)* (ELSE COLON block)?
    """
    def parse_elif_conditions(self, parser, tokens):
        conditions = []
        while not tokens.is_end() and tokens.current().name == 'ELIF':
            tokens.consume_expected('ELIF')
            cond = Expression().parse(parser, tokens)
            if cond == None:
                raise ParserError('Expected `elif` condition', tokens.current())
            tokens.consume_expected('COLON')
            block = Block().parse(parser, tokens)
            if block == None:
                raise ParserError('Expected `elif` body', tokens.current())
            conditions.append(ConditionElif(cond, block))
        return conditions

    def parse_else(self, parser, tokens):
        else_block = None
        if not tokens.is_end() and tokens.current().name == 'ELSE':
            tokens.consume_expected('ELSE')
            else_block = Block().parse(parser, tokens)
            if else_block is None:
                raise ParserError('Excepted `else` body', tokens.current())
        return else_block

    def parse(self, parser, tokens):
        tokens.consume_expected('IF')
        cond = Expression().parse(parser, tokens)
        if cond is None:
            raise ParserError('Excepted `if` condition', tokens.current())
        tokens.consume_expected('COLON')
        if_block = Block().parse(parser, tokens)
        if if_block is None:
            raise ParserError('Excepted if body', tokens.current())
        elif_conditions = self.parse_elif_conditions(parser, tokens)
        else_block = self.parse_else(parser, tokens)
        return ast.Condition(cond, if_block, elif_conditions, else_block)

class MatchStatement(Subparser):
    """match_stmt:
        MATCH expr COLON NEWLINE INDENT match_when+ (ELSE COLON block)? DEDENT
    """
    def parse_when(self, parser, tokens):
        tokens.consume_expected('WHEN')
        pattern = Expression().parse(parser, tokens)
        if pattern is None:
            raise ParserError('pattern expression expected', tokens.current())
        tokens.consume_expected('COLON')
        block = Block().parse(parser, tokens)
        return MatchPattern(pattern, block)

    def parse(self, parser, tokens):
        tokens.consume_expected('MATCH')
        cond = Expression().parse(parser, tokens)
        tokens.consume_expected('COLON', 'NEWLINE', 'INDENT')
        patterns = []
        while not tokens.is_end() and tokens.current().name == 'WHEN':
            patterns.append(self.parse_when(parser, tokens))
        if not patterns:
            raise ParserError('One or more `when` pattern excepted', tokens.current())
        else_block = None
        if not tokens.is_end() and tokens.current().name == 'ELSE':
            tokens.consume_expected('ELSE', 'COLON')
            else_block = Block().parse(parser, tokens)
            if else_block is None:
                raise ParserError('Excepted `else` body', tokens.current())
        tokens.consume_expected('DEDENT')
        return ast.Match(cond, patterns, else_block)

class WhileLoopStatement(Subparser):
    """loop_while_stmt: WHILE expr COLON block"""
    def parse(self, parser, tokens):
        tokens.consume_expected('WHILE')
        cond = Expression().parse(parser, tokens)
        if cond is None:
            raise ParserError('While condition expected', tokens.current())
        tokens.consume_expected('COLON')
        with enter_scope(parser, 'loop'):
            block = Block().parse(parser, tokens)
        if block is None:
            raise ParserError('Expected loop body', tokens.current())
        return ast.WhieLoop(cond, block)

class ForLoopStatement(Subparser):
    """loop_for_stmt: FOR NAME expr COLON block"""
    def parse(self, parser, tokens):
        tokens.consume_expected('FOR')
        id_token = tokens.consume_expected('NAME')
        tokens.consume_expected('IN')
        collection = Expression().parse(parser, tokens)
        tokens.consume_expected('COLON')
        with enter_scope(parser, 'loop'):
            block = Block().parse(parser, tokens)
        if block is None:
            raise ParserError('Excepted loop body', tokens.current())
        return ast.ForLoop(id_token.value, collection, block)
<commit_msg>chore(Statement): add return, break and continue statement<commit_after>#!/usr/bin/env python
# -*- encoding: utf-8 -*-
#
# Copyright (c) 2016 ASMlover. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
#  * Redistributions of source code must retain the above copyright
#    notice, this list ofconditions and the following disclaimer.
#
#  * Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in
#    the documentation and/or other materialsprovided with the
#    distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

from collections import namedtuple
import tyr_ast as ast
from tyr_error import TyrSyntaxError

MatchPattern = namedtuple('Match', ['pattern', 'body'])
ConditionElif = namedtuple('ConditionElif', ['cond', 'body'])

class ParserError(TyrSyntaxError):
    def __init__(self, message, token):
        super(ParserError, self).__init__(token.lineno, token.column, message)

def enter_scope(parser, name):
    class State(object):
        def __enter__(self):
            parser.scope.append(name)

        def __exit__(self, exc_type, exc_val, exc_tb):
            parser.scope.pop()

    return State()

class Subparser(object):
    PRECEDENCE = {
        'call': 10,
        'subscript': 10,

        'unary': 9,

        '*': 7,
        '/': 7,
        '%': 7,

        '+': 6,
        '-': 6,

        '>': 5,
        '>= ': 5,
        '<': 5,
        '<=': 5,

        '==': 4,
        '!=': 4,

        '&&': 3,

        '||': 2,

        '..': 1,
        '...': 1,
    }

    def get_subparser(self, token, subparsers, default=None):
        cls = subparsers.get(token.name, default)
        if cls:
            return cls()

class PrefixSubparser(Subparser):
    def parse(self, parser, tokens):
        raise NotImplementedError()

class InfixSubparser(Subparser):
    def parse(self, parser, tokens, left):
        raise NotImplementedError()

    def get_precedence(self, token):
        raise NotImplementedError()

class NumberExpression(PrefixSubparser):
    """number_expr: NUMBER"""
    def parse(self, parser, tokens):
        token = tokens.consume_expected('NUMBER')
        return ast.Number(token.value)

class StringExpression(PrefixSubparser):
    """str_expr: STRING"""
    def parse(self, parser, tokens):
        token = tokens.consume_expected('STRING')
        return ast.String(token.value)

class NameExpression(PrefixSubparser):
    """name_expr: NAME"""
    def parse(self, parser, tokens):
        token = tokens.consume_expected('NAME')
        return ast.Identifier(token.value)

class UnaryOperatorExpression(PrefixSubparser):
    """prefix_expr: OPERATOR expr"""
    SUPPORTED_OPERATORS = ['-', '!']
    def parse(self, parser, tokens):
        token = tokens.consume_expected('OPERATOR')
        if token.value not in self.SUPPORTED_OPERATORS:
            raise ParserError('Unary operator {} is not supported'.format(token.value), token)
        right = Expression().parse(parser, tokens, self.get_precedence(token))
        if not right:
            raise ParserError('Excepted expression'.format(token.value), tokens.consume())
        return ast.UnaryOperator(token.value, right)

    def get_precedence(self, token):
        return self.PRECEDENCE['unary']

class GroupExpression(PrefixSubparser):
    """group_expr: LPAREN expr RPAREN"""
    def parse(self, parser, tokens):
        tokens.consume_expected('LPAREN')
        right = Expression().parse(parser, tokens)
        tokens.consume_expected('RPAREN')
        return right

class ArrayExpression(PrefixSubparser):
    """array_epxr: LBRACK list_of_expr? RBRACK"""
    def parse(self, parser, tokens):
        tokens.consume_expected('LBRACK')
        items = ListOfExpressions().parse(parser, tokens)
        tokens.consume_expected('RBRACK')
        return ast.Array(items)

class DictionaryExpression(PrefixSubparser):
    """dict_expr: LCBRACK (expr COLON expr COMMA)* RCBRACK"""
    def parse_keyvalues(self, parser, tokens):
        items = []
        while not tokens.is_end():
            key = Expression().parse(parser, tokens)
            if key:
                tokens.consume_expected('COLON')
                value = Expression().parse(parser, tokens)
                if value is None:
                    raise ParserError('Dictionary value excepted', tokens.consume())
                items.append((key, value))
            else:
                break
            if tokens.current().name == 'COMMA':
                tokens.consume_expected('COMMA')
            else:
                break
        return items

    def parse(self, parser, tokens):
        tokens.consume_expected('LCBRACK')
        items = self.parse_keyvalues(parser, tokens)
        tokens.consume_expected('RCBRACK')
        return ast.Dictionary(items)

class BinaryOperatorExpression(InfixSubparser):
    """infix_expr: expr OPERATOR expr"""
    def parse(self, parser, tokens, left):
        token = tokens.consume_expected('OPERATOR')
        right = Expression().parse(parser, tokens, self.get_precedence(token))
        if right is None:
            raise ParserError('Excepted expression'.format(token.value), tokens.consume())
        return ast.BinaryOperator(token.value, left, right)

    def get_precedence(self, token):
        return self.PRECEDENCE[token.value]

class CallExpression(InfixSubparser):
    """call_expr: NAME LPAREN list_of_expr? RPAREN"""
    def parse(self, parser, tokens, left):
        tokens.consume_expected('LPAREN')
        arguments = ListOfExpressions().parse(parser, tokens)
        tokens.consume_expected('RPAREN')
        return ast.Call(left, arguments)

    def get_precedence(self, token):
        return self.PRECEDENCE['call']

class SubscriptOperatorExpression(InfixSubparser):
    """subscript_expr: NAME LBRACK expr RBRACK"""
    def parse(self, parser, tokens, left):
        tokens.consume_expected('LBRACK')
        key = Expression().parse(parser, tokens)
        if key is None:
            raise ParserError('Subscript operator key is required', tokens.current())
        tokens.consume_expected('RBRACK')
        return ast.SubscriptOperator(left, key)

    def get_precedence(self, token):
        return self.PRECEDENCE['subscript']

class Expression(Subparser):
    """expr:
        number_expr | str_expr | name_expr | group_expr |
        array_epxr | dict_expr | prefix_expr | infix_expr |
        call_expr | subscript_expr
    """
    def get_prefix_subparser(self, token):
        return self.get_subparser(token, {
            'NUMBER': NumberExpression,
            'STRING': StringExpression,
            'NAME': NameExpression,
            'LPAREN': GroupExpression,
            'LBRACK': ArrayExpression,
            'LCBRACK': DictionaryExpression,
            'OPERATOR': UnaryOperatorExpression,
        })

    def get_infix_subparser(self, token):
        return self.get_subparser(token, {
            'OPERATOR': BinaryOperatorExpression,
            'LPAREN': CallExpression,
            'LBRACK': SubscriptOperatorExpression,
        })

    def get_next_precedence(self, tokens):
        if not tokens.is_end():
            token = token.current()
            parser = self.get_infix_subparser(token)
            if parser != None:
                return parser.get_precedence(token)
        return 0

    def parse(self, parser, tokens, precedence=0):
        subparser = self.get_prefix_subparser(tokens.current())
        if subparser != None:
            left = subparser.parse(parser, tokens)
            if left != None:
                while precedence < self.get_next_precedence(tokens):
                    op = self.get_infix_subparser(token.current()).parse(parser, tokens, left)
                    if op != None:
                        left = op
                return left

class ListOfExpressions(Subparser):
    """list_of_expr: (expr COMMA)*"""
    def parse(self, parser, tokens):
        items = []
        while not tokens.is_end():
            exp = Expression().parse(parser, tokens)
            if exp != None:
                items.append(exp)
            else:
                break
            if tokens.current().name == 'COMMA':
                tokens.consume_expected('COMMA')
            else:
                break
        return items

class Block(Subparser):
    """block: NEWLINE INDENT stmts DEDENT"""
    def parse(self, parser, tokens):
        tokens.consume_expected('NEWLINE', 'INDENT')
        statements = Statements().parse(parser, tokens)
        tokens.consume_expected('DEDENT')
        return statements

class FunctionStatement(Subparser):
    """func_stmt: FUNCTION NAME LPAREN func_params? RPAREN COLON block"""
    def parse_params(self, tokens):
        params = []
        if tokens.current().name == 'NAME':
            while not tokens.is_end():
                id_token = tokens.consume_expected('NAME')
                params.append(id_token.value)
                if tokens.current().name == 'COMMA':
                    tokens.consume_expected('COMMA')
                else:
                    break
        return params

    def parse(self, parser, tokens):
        tokens.consume_expected('FUNCTION')
        id_token = tokens.consume_expected('NAME')
        tokens.consume_expected('LPAREN')
        arguments = self.parse_params(tokens)
        tokens.consume_expected('RPAREN', 'COLON')
        with enter_scope(parser, 'function'):
            block = Block().parse(parser, tokens)
        if block is None:
            raise ParserError('Excepted function body', tokens.current())
        return ast.Function(id_token.value, arguments, block)

class ConditionStatement(Subparser):
    """cond_stmt:
        IF exp COLON block (ELIF exp COLON block)* (ELSE COLON block)?
    """
    def parse_elif_conditions(self, parser, tokens):
        conditions = []
        while not tokens.is_end() and tokens.current().name == 'ELIF':
            tokens.consume_expected('ELIF')
            cond = Expression().parse(parser, tokens)
            if cond == None:
                raise ParserError('Expected `elif` condition', tokens.current())
            tokens.consume_expected('COLON')
            block = Block().parse(parser, tokens)
            if block == None:
                raise ParserError('Expected `elif` body', tokens.current())
            conditions.append(ConditionElif(cond, block))
        return conditions

    def parse_else(self, parser, tokens):
        else_block = None
        if not tokens.is_end() and tokens.current().name == 'ELSE':
            tokens.consume_expected('ELSE')
            else_block = Block().parse(parser, tokens)
            if else_block is None:
                raise ParserError('Excepted `else` body', tokens.current())
        return else_block

    def parse(self, parser, tokens):
        tokens.consume_expected('IF')
        cond = Expression().parse(parser, tokens)
        if cond is None:
            raise ParserError('Excepted `if` condition', tokens.current())
        tokens.consume_expected('COLON')
        if_block = Block().parse(parser, tokens)
        if if_block is None:
            raise ParserError('Excepted if body', tokens.current())
        elif_conditions = self.parse_elif_conditions(parser, tokens)
        else_block = self.parse_else(parser, tokens)
        return ast.Condition(cond, if_block, elif_conditions, else_block)

class MatchStatement(Subparser):
    """match_stmt:
        MATCH expr COLON NEWLINE INDENT match_when+ (ELSE COLON block)? DEDENT
    """
    def parse_when(self, parser, tokens):
        tokens.consume_expected('WHEN')
        pattern = Expression().parse(parser, tokens)
        if pattern is None:
            raise ParserError('pattern expression expected', tokens.current())
        tokens.consume_expected('COLON')
        block = Block().parse(parser, tokens)
        return MatchPattern(pattern, block)

    def parse(self, parser, tokens):
        tokens.consume_expected('MATCH')
        cond = Expression().parse(parser, tokens)
        tokens.consume_expected('COLON', 'NEWLINE', 'INDENT')
        patterns = []
        while not tokens.is_end() and tokens.current().name == 'WHEN':
            patterns.append(self.parse_when(parser, tokens))
        if not patterns:
            raise ParserError('One or more `when` pattern excepted', tokens.current())
        else_block = None
        if not tokens.is_end() and tokens.current().name == 'ELSE':
            tokens.consume_expected('ELSE', 'COLON')
            else_block = Block().parse(parser, tokens)
            if else_block is None:
                raise ParserError('Excepted `else` body', tokens.current())
        tokens.consume_expected('DEDENT')
        return ast.Match(cond, patterns, else_block)

class WhileLoopStatement(Subparser):
    """loop_while_stmt: WHILE expr COLON block"""
    def parse(self, parser, tokens):
        tokens.consume_expected('WHILE')
        cond = Expression().parse(parser, tokens)
        if cond is None:
            raise ParserError('While condition expected', tokens.current())
        tokens.consume_expected('COLON')
        with enter_scope(parser, 'loop'):
            block = Block().parse(parser, tokens)
        if block is None:
            raise ParserError('Expected loop body', tokens.current())
        return ast.WhieLoop(cond, block)

class ForLoopStatement(Subparser):
    """loop_for_stmt: FOR NAME expr COLON block"""
    def parse(self, parser, tokens):
        tokens.consume_expected('FOR')
        id_token = tokens.consume_expected('NAME')
        tokens.consume_expected('IN')
        collection = Expression().parse(parser, tokens)
        tokens.consume_expected('COLON')
        with enter_scope(parser, 'loop'):
            block = Block().parse(parser, tokens)
        if block is None:
            raise ParserError('Excepted loop body', tokens.current())
        return ast.ForLoop(id_token.value, collection, block)

class ReturnStatement(Subparser):
    """return_stmt: RETURN expr?"""
    def parse(self, parser, tokens):
        if not parser.scope or 'function' not in parser.scope:
            raise ParserError('Return outside of function', tokens.current())
        tokens.consume_expected('RETURN')
        value = Expression().parse(parser, tokens)
        tokens.consume_expected('NEWLINE')
        return ast.Return(value)

class BreakStatement(Subparser):
    """break_stmt: BREAK"""
    def parse(self, parser, tokens):
        if not parser.scope or parser.scope[-1] != 'loop':
            raise ParserError('Break outside of loop', tokens.current())
        tokens.consume_expected('BREAK', 'NEWLINE')
        return ast.Break()

class ContinueStatement(Subparser):
    """continue_stmt: CONTINUE"""
    def parse(self, parser, tokens):
        if not parser.scope or parser.scope[-1] != 'loop':
            raise ParserError('Continue outside of loop', tokens.current())
        tokens.consume_expected('CONTINUE', 'NEWLINE')
        return ast.Continue()
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
from __future__ import print_function
from __future__ import unicode_literals
from __future__ import division

import copy
from datetime import datetime

from django.conf import settings
from django.contrib.staticfiles import finders
from django.utils.timezone import now
from django.utils.translation import ugettext as _

from treemap.units import Convertible
from treemap.util import get_last_visited_instance, leaf_models_of_class
from treemap.models import InstanceUser

REPLACEABLE_TERMS = {
    'Resource': {'singular': _('Resource'),
                 'plural': _('Resources')}
}

def global_settings(request):
    last_instance = get_last_visited_instance(request)
    if hasattr(request, 'user') and request.user.is_authenticated():
        last_effective_instance_user =\
            request.user.get_effective_instance_user(last_instance)
        _update_last_seen(last_effective_instance_user)
    else:
        if hasattr(request, 'instance'):
            instance = request.instance
            default_role = instance.default_role

            last_effective_instance_user = InstanceUser(
                role=default_role, instance=instance)
        else:
            last_effective_instance_user = None

    if hasattr(request, 'instance') and request.instance.logo:
        logo_url = request.instance.logo.url
    else:
        logo_url = settings.STATIC_URL + "img/logo.png"

    try:
        comment_file_path = finders.find('version.txt')
        with open(comment_file_path, 'r') as f:
            header_comment = f.read()
    except:
        header_comment = "Version information not available\n"

    term = copy.copy(REPLACEABLE_TERMS)
    if hasattr(request, 'instance'):
        term.update(request.instance.config.get('terms', {}))
        for clz in leaf_models_of_class(Convertible):
            term.update({
                clz.__name__: clz.terminology(request.instance)})

    ctx = {
        'SITE_ROOT': settings.SITE_ROOT,
        'settings': settings,
        'last_instance': last_instance,
        'last_effective_instance_user': last_effective_instance_user,
        'logo_url': logo_url,
        'header_comment': header_comment,
        'term': term,
        'datepicker_start_date': datetime.min.replace(year=1900),
    }

    return ctx

def _update_last_seen(last_effective_instance_user):
    # Update the instance user's "last seen" date if necessary.
    # Done here instead of in middleware to avoid looking up
    # the request's InstanceUser again.
    iu = last_effective_instance_user
    today = now().date()
    if iu and iu.id and (not iu.last_seen or iu.last_seen < today):
        iu.last_seen = today
        iu.save_base()
<commit_msg>Added a comment about the term context variable<commit_after># -*- coding: utf-8 -*-
from __future__ import print_function
from __future__ import unicode_literals
from __future__ import division

import copy
from datetime import datetime

from django.conf import settings
from django.contrib.staticfiles import finders
from django.utils.timezone import now
from django.utils.translation import ugettext as _

from treemap.units import Convertible
from treemap.util import get_last_visited_instance, leaf_models_of_class
from treemap.models import InstanceUser

REPLACEABLE_TERMS = {
    'Resource': {'singular': _('Resource'),
                 'plural': _('Resources')}
}

def global_settings(request):
    last_instance = get_last_visited_instance(request)
    if hasattr(request, 'user') and request.user.is_authenticated():
        last_effective_instance_user =\
            request.user.get_effective_instance_user(last_instance)
        _update_last_seen(last_effective_instance_user)
    else:
        if hasattr(request, 'instance'):
            instance = request.instance
            default_role = instance.default_role

            last_effective_instance_user = InstanceUser(
                role=default_role, instance=instance)
        else:
            last_effective_instance_user = None

    if hasattr(request, 'instance') and request.instance.logo:
        logo_url = request.instance.logo.url
    else:
        logo_url = settings.STATIC_URL + "img/logo.png"

    try:
        comment_file_path = finders.find('version.txt')
        with open(comment_file_path, 'r') as f:
            header_comment = f.read()
    except:
        header_comment = "Version information not available\n"

    term = copy.copy(REPLACEABLE_TERMS)
    if hasattr(request, 'instance'):
        term.update(request.instance.config.get('terms', {}))
        # config.get('terms') above populates the term context variable with
        # model terminology provided it has been customized for the treemap
        # instance, but fails to populate it with the default terminology. The
        # for loop below ensures that term is populated with model terminology
        # whether it has been customized or not.

        # Convertible is the base class where the terminology class property is
        # defined, so its leaf subclasses are the ones with default terminology
        # we might care about.

        # leaf_models_of_class uses recursive descent through the
        # clz.__subclasses__ attributes, but it only iterates through a total
        # of around ten nodes at present, so it is unlikely to be a performance
        # problem.
        for clz in leaf_models_of_class(Convertible):
            term.update({
                clz.__name__: clz.terminology(request.instance)})

    ctx = {
        'SITE_ROOT': settings.SITE_ROOT,
        'settings': settings,
        'last_instance': last_instance,
        'last_effective_instance_user': last_effective_instance_user,
        'logo_url': logo_url,
        'header_comment': header_comment,
        'term': term,
        'datepicker_start_date': datetime.min.replace(year=1900),
    }

    return ctx

def _update_last_seen(last_effective_instance_user):
    # Update the instance user's "last seen" date if necessary.
    # Done here instead of in middleware to avoid looking up
    # the request's InstanceUser again.
    iu = last_effective_instance_user
    today = now().date()
    if iu and iu.id and (not iu.last_seen or iu.last_seen < today):
        iu.last_seen = today
        iu.save_base()
<|endoftext|>


<commit_before>from django.conf import settings

from treemap.util import get_last_visited_instance

def global_settings(request):
    last_instance = get_last_visited_instance(request)
    if last_instance and last_instance.logo:
        logo_url = last_instance.logo.url
    else:
        logo_url = settings.STATIC_URL + "img/logo-beta.png"

    ctx = {'SITE_ROOT': settings.SITE_ROOT,
           'settings': settings,
           'last_instance': last_instance,
           'logo_url': logo_url}

    if hasattr(request, 'instance') and request.user.is_authenticated():
        ctx['instance_user'] = request.user.get_instance_user(request.instance)

    return ctx
<commit_msg>Use standard logo on non-instance pages, fixes #702<commit_after>from django.conf import settings

from treemap.util import get_last_visited_instance

def global_settings(request):
    last_instance = get_last_visited_instance(request)
    if hasattr(request, 'instance') and request.instance.logo:
        logo_url = request.instance.logo.url
    else:
        logo_url = settings.STATIC_URL + "img/logo-beta.png"

    ctx = {'SITE_ROOT': settings.SITE_ROOT,
           'settings': settings,
           'last_instance': last_instance,
           'logo_url': logo_url}

    if hasattr(request, 'instance') and request.user.is_authenticated():
        ctx['instance_user'] = request.user.get_instance_user(request.instance)

    return ctx
<|endoftext|>


<commit_before> __author__ = '700608667'

from cloudify import ctx
from cloudify.decorators import operation

@operation
def simplelogger(**kwargs):
    ctx.logger.info("*** running simplelogger ***")
    ctx.logger.info("    simplelogger found node with id {} type {} and properties".format(ctx.node.id, ctx.type, ctx.node.properties))
    ctx.logger.info("*** completed simplelogger ***")<commit_msg>Removed blank space from first line.<commit_after>__author__ = '700608667'

from cloudify import ctx
from cloudify.decorators import operation

@operation
def simplelogger(**kwargs):
    ctx.logger.info("*** running simplelogger ***")
    ctx.logger.info("    simplelogger found node with id {} type {} and properties".format(ctx.node.id, ctx.type, ctx.node.properties))
    ctx.logger.info("*** completed simplelogger ***")<|endoftext|>


<commit_before>from django.conf.urls import *

urlpatterns = patterns('corehq.apps.importer.views',
    url(r'^excel/config/$', 'excel_config', name='excel_config'),
    url(r'^excel/fields/$', 'excel_fields', name='excel_fields'),
    url(r'^excel/commit/$', 'excel_commit', name='excel_commit'),
    url(r'^importer_ajax/(?P<download_id>[0-9a-fA-Z]{25,32})/$',
        'importer_job_poll', name='importer_job_poll'),
)
<commit_msg>update importer urls<commit_after>from django.conf.urls import *

from corehq.apps.importer.views import (
    excel_commit,
    excel_config,
    excel_fields,
    importer_job_poll,
)

urlpatterns = patterns('corehq.apps.importer.views',
    url(r'^excel/config/$', excel_config, name='excel_config'),
    url(r'^excel/fields/$', excel_fields, name='excel_fields'),
    url(r'^excel/commit/$', excel_commit, name='excel_commit'),
    url(r'^importer_ajax/(?P<download_id>[0-9a-fA-Z]{25,32})/$',
        importer_job_poll, name='importer_job_poll'),
)
<|endoftext|>


<commit_before>from django.conf import settings
from django.utils.cache import _generate_cache_header_key
from corehq.util.quickcache import quickcache, QuickCache

DEFAULT_EXPIRY = 60 * 60  # an hour
CACHE_PREFIX = 'hq.reports'  # a namespace where cache keys go

class _ReportQuickCache(QuickCache):
    """
    Just like QuickCache, but intercepts the function call to abort caching
    under certain conditions
    """
    def __call__(self, *args, **kwargs):
        report = args[0]
        if report.is_cacheable and _is_valid(report) and settings.CACHE_REPORTS:
            return super(_ReportQuickCache, self).__call__(*args, **kwargs)
        else:
            return self.fn(*args, **kwargs)

def _is_valid(report):
    """
    checks if this meets the preconditions for being allowed in the cache
    """
    try:
        return (
            report.request.domain
            and report.request.couch_user._id
            and report.request.get_full_path().startswith(
                '/a/{domain}/'.format(domain=report.request.domain)
            )
        )
    except AttributeError:
        return False

def _custom_vary_on(report):
    """
    signature is intentionally restricted to a single argument
    to prevent @request_cache() from decorating a method that has non-self args
    """
    return [
        _generate_cache_header_key(CACHE_PREFIX, report.request),
        report.request.domain,
        report.request.couch_user._id,
    ]

def request_cache(expiry=DEFAULT_EXPIRY):
    """
    A decorator that can be used on a method of a GenericReportView subclass

    or any other class that provides the following properties:
      - self.request (a django request object, with .domain and .couch_user)
      - self.is_cacheable (boolean)

    """

    return quickcache(vary_on=_custom_vary_on,
                      timeout=expiry, helper_class=_ReportQuickCache)
<commit_msg>Update ReportQuickCache to cache b3 filters separately<commit_after>from django.conf import settings
from django.utils.cache import _generate_cache_header_key
from corehq.util.quickcache import quickcache, QuickCache

DEFAULT_EXPIRY = 60 * 60  # an hour
CACHE_PREFIX = 'hq.reports'  # a namespace where cache keys go

class _ReportQuickCache(QuickCache):
    """
    Just like QuickCache, but intercepts the function call to abort caching
    under certain conditions
    """
    def __call__(self, *args, **kwargs):
        report = args[0]
        self.prefix = '{}{}'.format(self.prefix, '/b3' if report.is_bootstrap3 else '')
        if report.is_cacheable and _is_valid(report) and settings.CACHE_REPORTS:
            return super(_ReportQuickCache, self).__call__(*args, **kwargs)
        else:
            return self.fn(*args, **kwargs)

def _is_valid(report):
    """
    checks if this meets the preconditions for being allowed in the cache
    """
    try:
        return (
            report.request.domain
            and report.request.couch_user._id
            and report.request.get_full_path().startswith(
                '/a/{domain}/'.format(domain=report.request.domain)
            )
        )
    except AttributeError:
        return False

def _custom_vary_on(report):
    """
    signature is intentionally restricted to a single argument
    to prevent @request_cache() from decorating a method that has non-self args
    """
    return [
        _generate_cache_header_key(CACHE_PREFIX, report.request),
        report.request.domain,
        report.request.couch_user._id,
    ]

def request_cache(expiry=DEFAULT_EXPIRY):
    """
    A decorator that can be used on a method of a GenericReportView subclass

    or any other class that provides the following properties:
      - self.request (a django request object, with .domain and .couch_user)
      - self.is_cacheable (boolean)

    """

    return quickcache(vary_on=_custom_vary_on,
                      timeout=expiry, helper_class=_ReportQuickCache)
<|endoftext|>