Exemple #1
0
    def _GenerateRegisteredCashFlows(self):
        """
        Create cash flows based on those previously registered.

        :return:
        """
        Logger('Model._GenerateRegisteredCashFlows()')
        Logger('Adding {0} cash flows to sectors',
               priority=3,
               data_to_format=(len(self.RegisteredCashFlows), ))
        for source_sector, target_sector, amount_variable, is_income_source, is_income_dest in self.RegisteredCashFlows:
            is_cross_currency = source_sector.CurrencyZone != target_sector.CurrencyZone
            if is_cross_currency:
                if self.ExternalSector is None:
                    msg = """Only can have cross-currency flows if an ExternalSector object is created\nSource={0} Destination={1}""".format(
                        source_sector.FullCode, target_sector.FullCode)
                    raise LogicError(msg)
            full_variable_name = source_sector.GetVariableName(amount_variable)
            source_sector.AddCashFlow('-' + full_variable_name,
                                      eqn=None,
                                      is_income=is_income_source)
            if is_cross_currency:
                fx = self.ExternalSector['FX']
                fx._SendMoney(source_sector, full_variable_name)
                term = fx._ReceiveMoney(target_sector=target_sector,
                                        source_sector=source_sector,
                                        variable_name=full_variable_name)
            else:
                term = '+' + full_variable_name
            target_sector.AddCashFlow(term, eqn=None, is_income=is_income_dest)
Exemple #2
0
 def _GenerateEquations(self):
     """
     Generate equation.
      :return: None
     """
     Logger('MoneyMarket _GenerateEquations', priority=5)
     dem_name = 'DEM_' + self.Code
     self.AddVariable(dem_name, 'Total demand for ' + self.LongName, '')
     dem_terms = []
     for s in self.SearchListSource.GetSectors():
         if not s.HasF:
             continue
         if s.Code == self.IssuerShortCode:
             Logger('Found Issuer', priority=3)
             s.AddVariable('SUP_' + self.Code, 'Supply of ' + self.LongName,
                           self.GetVariableName(dem_name))
             self.AddVariable('SUP_' + self.Code,
                              'Supply of ' + self.LongName,
                              s.GetVariableName('SUP_' + self.Code))
             continue
         try:
             # If the Sector already has demand for this, add the term.
             term = s.GetVariableName(dem_name)
             self.AddTermToEquation(dem_name, term)
             # dem_terms.append(term)
             continue
         except KeyError:
             pass
         Logger('Sector is missing demand for {0}; automatically filled in',
                priority=3,
                data_to_format=(dem_name, ))
         s.AddVariable(dem_name, 'Demand for ' + self.LongName,
                       s.GetVariableName('F'))
         self.AddTermToEquation(dem_name, s.GetVariableName(dem_name))
Exemple #3
0
 def _FinalSteps(self):  # pragma: no cover
     self.EquationSolver.ParseString(self.FinalEquations)
     self.EquationSolver.SolveEquation()
     self.LogInfo()
     self.State = 'Finished Running'
     Logger(self.EquationSolver.GenerateCSVtext(), 'timeseries')
     Logger.cleanup()
Exemple #4
0
 def AddVariable(self, varname, desc='', eqn=''):
     """
     Add a variable to the sector.
     The variable name (varname) is the local name; it will be decorated to create a
     full name. Equations within a sector can use the local name; other sectors need to
     use GetVariableName to get the full name.
     :param varname: str
     :param desc: str
     :param eqn: str
     :return: None
     """
     if '__' in varname:
         raise ValueError('Cannot use "__" inside local variable names: ' +
                          varname)
     if desc is None:
         desc = ''
     if type(eqn) == Equation:
         equation = eqn
     else:
         equation = Equation(varname, desc, [
             Term(eqn, is_blob=True),
         ])
     if varname in self.GetVariables():
         Logger('[ID={0}] Variable Overwritten: {1}',
                priority=3,
                data_to_format=(self.ID, varname))
     self.EquationBlock.AddEquation(equation)
     # self.Equations[varname] = eqn
     Logger('[ID={0}] Variable Added: {1} = {2} # {3}',
            priority=2,
            data_to_format=(self.ID, varname, eqn, desc))
Exemple #5
0
 def test_solver_logging(self):
     Logger.cleanup()
     mock = MockFile()
     Logger.log_file_handles = {'step': mock}
     obj = EquationSolver()
     obj.TraceStep = 2
     obj.RunEquationReduction = False
     # By forcing 't' into the variable list, no automatic creation of time variables
     obj.ParseString("""
          x=t
          z=x+1
          z(0) = 2.
          exogenous
          t=[10.]*20
          MaxTime=3""")
     obj.ExtractVariableList()
     obj.SetInitialConditions()
     self.assertEqual([], mock.buffer)
     obj.SolveStep(1)
     self.assertEqual([], mock.buffer)
     obj.SolveStep(2)
     # I do not care what the contents are; I just want to validate
     # that it triggers
     self.assertTrue(len(mock.buffer) > 0)
     # Reset the buffer
     mock.buffer = []
     obj.SolveStep(3)
     self.assertEqual([], mock.buffer)
Exemple #6
0
 def _GenerateEquations(self):
     # self.AddVariable('SUP_GOOD', 'Supply of goods', '<TO BE DETERMINED>')
     wage_share = 1.0 - self.ProfitMargin
     Logger('Searching for Market Sector with Code {0} in parent country',
            priority=4,
            data_to_format=(self.OutputName, ))
     try:
         market_sup_good = self.Parent.LookupSector(
             self.OutputName).GetVariableName('SUP_' + self.OutputName)
     except KeyError:
         raise Warning('Business {0} Cannot Find Market for {1}'.format(
             self.Code, self.OutputName))
     if self.ProfitMargin == 0:
         self.AddVariable('DEM_' + self.LabourInputName,
                          'Demand for labour', market_sup_good)
         # self.Equations['PROF'] = ''
     else:
         self.AddVariable('DEM_' + self.LabourInputName,
                          'Demand for labour',
                          '%0.3f * %s' % (wage_share, market_sup_good))
         self.SetEquationRightHandSide(
             'PROF', '%0.3f * %s' % (self.ProfitMargin, market_sup_good))
     for s in self.Parent.SectorList:
         if 'DIV' in s.EquationBlock.Equations:
             Logger('Adding dividend flow', priority=5)
             self.AddCashFlow('-DIV',
                              'PROF',
                              'Dividends paid',
                              is_income=False)
             s.AddCashFlow('DIV',
                           self.GetVariableName('PROF'),
                           'Dividends received',
                           is_income=True)
             break
Exemple #7
0
 def test_empty(self):
     mock = MockFile()
     Logger.log_file_handles = {'log': mock}
     Logger('', endline=False)
     self.assertEqual([], mock.buffer)
     Logger('', endline=True)
     self.assertEqual([' \n'], mock.buffer)
     Logger.cleanup()
Exemple #8
0
    def _GenerateEquations(self):
        """
        Call _GenerateEquations on all child Sector objects.

        :return:
        """
        Logger('Model._GenerateEquations()', priority=1)
        for cntry in self.CountryList:
            for sector in cntry.SectorList:
                Logger('Calling _GenerateEquations on {0}  ({1})',
                       priority=3,
                       data_to_format=(sector.FullCode, type(sector)))
                sector._GenerateEquations()
Exemple #9
0
 def test_register(self):
     Logger.cleanup()
     Logger.register_log('filename', 'log')
     self.assertEqual({'log': 'filename'}, Logger.log_file_handles)
     with self.assertRaises(ValueError):
         Logger.register_log('fname2', 'log')
     Logger.cleanup()
Exemple #10
0
    def _FinalEquationFormatting(self, out):
        """
        Convert equation information in list into formatted strings.

        :param out: list
        :return:
        """
        Logger('_FinalEquationFormatting()', priority=5)
        endo = []
        exo = []
        for row in out:
            if 'EXOGENOUS' in row[1]:
                new_eqn = row[1].replace('EXOGENOUS', '')
                exo.append((row[0], new_eqn, row[2]))
            else:
                endo.append(row)
        max0 = max([len(x[0]) for x in out])
        max1 = max([len(x[1]) for x in out])
        formatter = '%<max0>s = %-<max1>s  # %s'
        formatter = formatter.replace('<max0>', str(max0))
        formatter = formatter.replace('<max1>', str(max1))
        endo = [formatter % x for x in endo]
        exo = [formatter % x for x in exo]
        s = '\n'.join(endo) + '\n\n# Exogenous Variables\n\n' + '\n'.join(exo)
        s += '\n\nMaxTime = {0}\nErr_Tolerance=1e-6'.format(self.MaxTime)
        return s
Exemple #11
0
    def AddExogenous(self, sector_fullcode, varname, value):
        """
        Add an exogenous variable to the model. Overwrites an existing variable definition.
        Need to use the full sector code.

        Exogenous variables are sepcified as time series, which are implemented as list variables ([1, 2, 3,...])
        The exogenous variable can either be specified as a string (which can be evaluated to a list), or else
        as a list object. The list object will be converted into a string representation using repr(), which
        means that it may be much longer than using something like '[20,] * 100'.

        At present, does not support the usage of specifying a constant value. For example value='20.' does
        not work, you need '[20.]*100.'

        :param sector_fullcode: str
        :param varname: str
        :param value: str
        :return:
        """
        Logger('Adding exogenous variable: {0} in {1}',
               priority=5,
               data_to_format=(varname, sector_fullcode))
        # If the user passes in a list or tuple, convert it to a string representation.
        if type(value) in (list, tuple):
            value = repr(value)
        self.Exogenous.append((sector_fullcode, varname, value))
Exemple #12
0
    def _SearchSupplier(self):
        """
        Find the sector that is a single supplier in a country.
        Throws a LogicError if more than one, or none.

        Need to set SupplyAllocation if you want to do something not
        covered by this default behaviour.

        :return: Sector
        """
        Logger('Market {0} searching Country {1} for a supplier',
               priority=3,
               data_to_format=(self.Code, self.Parent.Code))
        ret_value = None
        for sector in self.Parent.GetSectors():
            if sector.ID == self.ID:
                continue
            if 'SUP_' + self.Code in sector.EquationBlock.Equations:
                if ret_value is None:
                    ret_value = sector
                else:
                    raise LogicError(
                        'More than one supplier, must set SupplyAllocation: ' +
                        self.Code)
        if ret_value is None:
            raise LogicError('No supplier: ' + self.Code)
        self.ResidualSupply = ret_value
        return ret_value
Exemple #13
0
    def _CreateFinalEquations(self):
        """
        Create Final equations.

        Final output, which is a text block of equations
        :return: str
        """
        Logger('Model._CreateFinalEquations()')
        out = []
        for cntry in self.CountryList:
            for sector in cntry.SectorList:
                out.extend(sector._CreateFinalEquations())
        out.extend(self._GenerateInitialConditions())
        out.extend(self.GlobalVariables)
        if len(out) == 0:
            self.FinalEquations = ''
            raise Warning('There are no equations in the system.')
        # Build the FinalEquationBlock
        self.FinalEquationBlock = EquationBlock()
        for row in out:
            if 'EXOGENOUS' in row[1]:
                eq = Equation(row[0],
                              desc=row[2],
                              rhs=row[1].replace('EXOGENOUS', ''))
            else:
                eq = Equation(row[0], desc=row[2], rhs=row[1])
            self.FinalEquationBlock.AddEquation(eq)
        out = self._FinalEquationFormatting(out)
        self.FinalEquations = out
        return out
Exemple #14
0
    def RegisterCashFlow(self,
                         source_sector,
                         target_sector,
                         amount_variable,
                         is_income_source=True,
                         is_income_dest=True):
        """
        Register a cash flow between two sectors.

        The amount_variable is the name of the local variable within the source sector.

        Only allowed across currency zones if an ExternalSector object has been defined;
        otherwise throws a LogicError.

        The currency value of the amount_variable is assumed to be in the source currency.

        :param is_income_dest:
        :param is_income_source:
        :param source_sector: Sector
        :param target_sector: Sector
        :param amount_variable: str
        :return:
        """
        # if amount_variable not in source_sector.Equations:
        #     raise KeyError('Must define the variable that is the amount of the cash flow')
        Logger('Cash flow registered {0}: {1} -> {2}  [ID: {3} -> {4}]',
               priority=3,
               data_to_format=(amount_variable, source_sector.Code,
                               target_sector.Code, source_sector.ID,
                               target_sector.ID))
        self.RegisteredCashFlows.append(
            (source_sector, target_sector, amount_variable, is_income_source,
             is_income_dest))
Exemple #15
0
    def _GenerateTermsLowLevel(self, prefix, long_desc):
        """
        Generate the terms associated with this market, for supply and demand.

        TODO: This is now only called for the demand function; simplify to just refer
        to demand.

        :param prefix: str
        :param long_desc: str
        :return: None
        """
        Logger('Searching for demand for market {0}',
               priority=3,
               data_to_format=(self.FullCode, ))
        if prefix not in ('SUP', 'DEM'):
            raise LogicError('Input to function must be "SUP" or "DEM"')
        # country = self.Parent
        short_name = prefix + '_' + self.Code
        long_name = prefix + '_' + self.FullCode
        self.AddVariable(short_name, long_desc + ' for Market ' + self.Code,
                         '')
        term_list = []
        for s in self.CurrencyZone.GetSectors():
            if s.ID == self.ID:
                continue
            if self.ShareParent(s):
                var_name = short_name
            else:
                var_name = long_name
            try:
                term = s.GetVariableName(var_name)
            except KeyError:
                Logger('Variable {0} does not exist in {1}',
                       priority=10,
                       data_to_format=(var_name, s.FullCode))
                continue
            term_list.append('+ ' + term)
            if prefix == 'SUP':  # pragma: no cover
                # Since we assume that there is a single supplier, we can set the supply equation to
                # point to the equation in the market.
                s.AddCashFlow(var_name, self.GetVariableName(var_name),
                              long_desc)
            else:
                # Must fill in demand equation in sectors.
                s.AddCashFlow('-' + var_name, '', long_desc)
        eqn = create_equation_from_terms(term_list)
        self.SetEquationRightHandSide(short_name, eqn)
Exemple #16
0
 def __init__(self, parent=None, code=''):
     self.ID = EconomicObject.ID
     EconomicObject.ID += 1
     self.Parent = parent
     self.Code = code
     self.LongName = ''
     Logger('EconomicObject Created: {0} ID = {1}',
            priority=1,
            data_to_format=(type(self), self.ID))
Exemple #17
0
 def _GenerateEquationsFrontEnd(self):  # pragma: no cover
     """
     Used by graphical front ends; generates a logging message. (In Model.Main(),
     the logging is done by the Model before it calls the Sector.)
     :return:
     """
     Logger('Running _GenerateEquations on {0} [{1}]',
            priority=3,
            data_to_format=(self.Code, self.ID))
     self._GenerateEquations()
Exemple #18
0
 def _FitIntoCurrencyZone(self, country):
     """
     Find whether Country fits into an existing CurrencyZone; if not,
     create a new one.
     :param country: Country
     :return: CurrencyZone
     """
     for czone in self.CurrencyZoneList:
         if country.Currency == czone.Currency:
             czone.CountryList.append(country)
             Logger('Fitting {0} into CurrencyZone {1}',
                    data_to_format=(country.LongName, czone.Currency))
             return czone
     Logger('Creating new currency zone {0}, adding {1} to it',
            data_to_format=(country.Currency, country.LongName))
     czone = CurrencyZone(self, country.Currency)
     czone.CountryList.append(country)
     self.CurrencyZoneList.append(czone)
     return czone
Exemple #19
0
 def _FixAliases(self):
     """
     Assign the proper names to variables in Sector objects (that were perviously aliases).
     :return:
     """
     Logger('Fixing aliases (Model._FixAliases)', priority=3)
     lookup = {}
     for alias in self.Aliases:
         sector, varname = self.Aliases[alias]
         lookup[alias] = sector.GetVariableName(varname)
     for sector in self.GetSectors():
         sector._ReplaceAliases(lookup)
    def _GenerateEquations(self):
        # self.AddVariable('SUP_GOOD', 'Supply of goods', '<TO BE DETERMINED>')
        wage_share = 1.0 - self.ProfitMargin
        Logger('Searching for Market Sector with Code {0} in parent country',
               priority=4,
               data_to_format=(self.OutputName, ))

        # Since we have inventories, profits are volume of sales * profit margin
        self.SetEquationRightHandSide('PROF', '.1 * SUP_GOOD')
        for s in self.Parent.SectorList:
            if s.Code == 'HH':
                Logger('Adding dividend flow', priority=5)
                self.AddCashFlow('-DIV',
                                 'DIVPAID',
                                 'Dividends paid',
                                 is_income=False)
                s.AddCashFlow('DIV',
                              self.GetVariableName('DIVPAID'),
                              'Dividends received',
                              is_income=True)
                break
Exemple #21
0
    def _RegisterAlias(self, alias, sector, local_variable_name):
        """
        Used by Sector objects to register aliases for local variables.

        :param alias: str
        :param sector: Sector
        :param local_variable_name: str
        :return:
        """
        Logger('Registering alias {0} for {1} in ID={2}',
               priority=5,
               data_to_format=(alias, local_variable_name, sector.ID))
        self.Aliases[alias] = (sector, local_variable_name)
Exemple #22
0
    def AddGlobalEquation(self, var, description, eqn):
        """
        Add a variable that is not associated with a sector.
        Typical example: 't'

        :param var: str
        :param description: str
        :param eqn: str
        :return: None
        """
        Logger('Registering global equation: {0} = {1}',
               priority=5,
               data_to_format=(var, eqn))
        self.GlobalVariables.append((var, eqn, description))
 def SolveStep(self, step):
     """
     Solve a step. (Assumed to be called in order.)
     :param step: int
     :return:
     """
     is_trace_step = step == self.TraceStep
     if is_trace_step:
         Logger('Starting convergence tracing.', log='step')
         Logger('Step {0}'.format(step), log='step')
         self.TimeSeriesStepTrace = TimeSeriesHolder('iteration')
         self.TimeSeriesStepTrace['iteration'] = []
         self.TimeSeriesStepTrace['iteration_error'] = []
         self.TimeSeriesStepTrace['iteration_abs_change'] = []
         Logger("""
     Values at beginning of step. (Only includes variables that are solved within
     iteration. Decorative variables calculated later).""",
                log='step')
     try:
         self._SolveStep(step, is_trace_step)
     finally:
         if is_trace_step:
             Logger(self.TimeSeriesStepTrace.GenerateCSVtext(), log='step')
Exemple #24
0
 def OnRunModel(self):
     name = self.GetModelName()
     if name is None:
         # Not sure how we get here, but, go back to chooser frame
         self.FrameChooser.tkraise()
         return
     self.CleanupOnModelChange()
     Logger.cleanup()
     if not self.Parameters.LogDir == '':
         base_name = os.path.join(self.Parameters.LogDir, name)
         Logger.register_standard_logs(base_file_name=base_name)
     python_mod = self.Importer(name)
     try:
         self.Model = python_mod.build_model()
     except Exception as e:
         messagebox.showinfo(message=str(e), icon='error', title='Error')
         raise e
     if type(self.Model) is not Model:
         raise ValueError('Expected a Model, got {0} instead'.format(
             type(Model)))
     self.Sectors = self.Model.GetSectors()
     self.UpdateModelViewer()
     self.FrameModelViewer.tkraise()
Exemple #25
0
    def main(self, base_file_name=None):  # pragma: no cover
        """
        Routine that does most of the work of model building. The model is build based upon
        the Sector objects that have been registered as children of this Model.

        The base_file_name is the base filename that is used for Logging operations; just used
        to call Logger.register_standard_logs(base_file_name). It is recommended that you call
        Logger.register_standard_logs() before main, so that Sector creation can be logged.

        The major operations:
        [1] Call GenerateEquations() on all Sectors. The fact that GenerateEquations() is only
         called now at the Sector level means that Sectors can be created independently, and the
        GenerateEquations() call which ties it to other sectors is only called after all other sectors
        are created. (There is at least one exception where Sectors have to be created in a specific
        order, which we want to avoid.)

        [2] Cleanup work: processing initial conditions, exogenous variables, replacing
            aliases in equations.

        [3] Tie the sector level equations into a single block of equations. (Currently strings, not
            Equation objects.)

        [4] The equations are passed to self.EquationSolver, and they are solved.

        The user can then use GetTimeSeries() to access the output time series (if they can be
        calculated.)

        :param base_file_name: str
        :return: None
        """
        self.State = 'Running'
        try:
            if base_file_name is not None:
                Logger.register_standard_logs(base_file_name)
            Logger('Starting Model main()')
            self._GenerateFullSectorCodes()
            self._GenerateEquations()
            self._FixAliases()
            self._GenerateRegisteredCashFlows()
            self._ProcessExogenous()
            self.FinalEquations = self._CreateFinalEquations()
            self.EquationSolver.ParseString(self.FinalEquations)
            self.EquationSolver.SolveEquation()
            self.LogInfo()
        except Warning as e:
            self.LogInfo(ex=e)
            print('Warning triggered: ' + str(e))
            return self.FinalEquations
        except Exception as e:
            self.LogInfo(ex=e)
            raise
        finally:
            self.State = 'Finished Running'
            Logger(self.EquationSolver.GenerateCSVtext(), 'timeseries')
            Logger.cleanup()
        return self.FinalEquations
Exemple #26
0
    def _GenerateFullSectorCodes(self):
        """
        Create full sector names (which is equal to '[country.Code]_[sector.Code]' - if there is more than one country.
        Equals the sector code otherwise.

        :return: None
        """
        Logger('Generating FullSector codes (Model._GenerateFullSectorCodes()',
               priority=3)
        add_country_code = len(self.CountryList) > 1
        for cntry in self.CountryList:
            for sector in cntry.SectorList:
                if add_country_code:
                    sector.FullCode = cntry.Code + '_' + sector.Code
                else:
                    sector.FullCode = sector.Code
Exemple #27
0
    def _AddSector(self, sector):
        """
        Add a sector to this country. This is called by the Sector constructore; users
        should not call directly.

        :param sector: Sector
        :return:
        """
        Logger('Adding Sector {0} To Country {1}',
               priority=1,
               data_to_format=(sector.Code, self.Code))
        if sector.Code in self:
            raise LogicError(
                'Sector with Code {0} already in Country {1}'.format(
                    sector.Code, self.Code))
        self.SectorList.append(sector)
Exemple #28
0
    def _AddCountry(self, country):
        """
        Add a country to the list. This is called by the object constructore; users should
        not call this.

        :param country: Country
        :return: None
        """
        Logger('Adding Country: {0} ID={1}',
               data_to_format=(country.Code, country.ID))
        if country.Code in self:
            raise LogicError('Country with Code {0} already in Model'.format(
                country.Code))
        self.CountryList.append(country)
        self.DefaultCurrency = country.Currency
        czone = self._FitIntoCurrencyZone(country)
        country.CurrencyZone = czone
Exemple #29
0
    def _ProcessExogenous(self):
        """
        Handles the exogenous variables.

        :return: None
        """
        Logger('Processing {0} exogenous variables',
               priority=3,
               data_to_format=(len(self.Exogenous), ))
        for sector_code, varname, eqn in self.Exogenous:
            if type(sector_code) is str:
                sector = self.LookupSector(sector_code)
            else:
                sector = sector_code
            if varname not in sector.EquationBlock.Equations:
                raise KeyError('Sector %s does not have variable %s' %
                               (sector_code, varname))
            # Need to mark exogenous variables
            sector.SetEquationRightHandSide(varname, 'EXOGENOUS ' + eqn)
Exemple #30
0
    def _GenerateInitialConditions(self):
        """
        Create block of equations for initial conditions.

        Validates that the variables exist.
        :return:
        """
        Logger('Generating {0} initial conditions',
               priority=3,
               data_to_format=(len(self.InitialConditions), ))
        out = []
        for sector_code, varname, value in self.InitialConditions:
            sector = self.LookupSector(sector_code)
            if varname not in sector.EquationBlock.Equations:
                raise KeyError('Sector %s does not have variable %s' %
                               (sector_code, varname))
            out.append(('%s(0)' % (sector.GetVariableName(varname), ), value,
                        'Initial Condition'))
        return out