Repository: TAMUparametric/energiapy (language: python)

Issue:
cost plotting stacked bar plot
Added stacked bar plots:

they have  a log scale

Example: 

![bb11e83d-d1e7-480d-a67d-216ce6997266](https://user-images.githubusercontent.com/59067790/226633316-66780cc5-8dfa-461b-acda-864ba959e8cf.png)




Discussion / hints:

Fix patch:
diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml
index 532184c2..a97b5c9a 100644
--- a/.github/workflows/python-package.yml
+++ b/.github/workflows/python-package.yml
@@ -35,3 +35,4 @@ jobs:
         flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
         # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
         flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
+
diff --git a/README.md b/README.md
index 14b94a6f..a75ca6f3 100644
--- a/README.md
+++ b/README.md
@@ -1,94 +1,97 @@
-# energiapy
-
-
-[![Documentation Status](https://readthedocs.org/projects/energiapy/badge/)](https://energiapy.readthedocs.io/en/latest/)
-[![PyPI](https://img.shields.io/pypi/v/energiapy.svg)](https://pypi.org/project/energiapy)
-[![Downloads](https://static.pepy.tech/personalized-badge/energiapy?period=total&units=international_system&left_color=grey&right_color=orange&left_text=Downloads)](https://pepy.tech/project/energiapy)
-
-energiapy is a tool for the multiscale modeling and optimization of energy systems. energiapy uses a resource task network (RTN) based approach to formulate mathematical programs as pyomo instances. Models can be solved using solvers, and the results can be analyzed within the energiapy framework. 
-Constituent functionalities have been built over the research conducted by the [Multiparameteric Optimization and Control (Pistikopoulos) Group](https://parametric.tamu.edu/).
-
-
-## Installation
-
-energiapy can be installed using the standard pip installation. It is recommended to install the package in a separate conda or pip environment. 
-
-Create and activate a conda environment 
-
-    conda create --name energiaenv python=3.10
-    conda activate energiaenv
-
-Or, create and activate a pip environment 
-
-    python3.10 -m venv energiaenv
-    .\energiaenv\Scripts\activate
-
-Install energiapy
-
-    pip install energiapy
-
-
-## Key Applications
-
-1. Design of future energy systems (network design)
-2. Scheduling under uncertainty
-3. Life-cycle and environmental impact assessment
-4. Techno-economic analysis 
-5. System resiliency and reliability characterization
-
-Models can also simultaneously assuage the above under an integrated paradigm.
-
-## Modeling Approach
-
-Model components are declared as energiapy objects. The suggested hierarchy is as follows:
-
-1. **Temporal_scale:** The multiple temporal scales being considered
-2. **Resource:** Resources can be in any quantifiable form such mass, energy, information
-3. **Material:** Infrastructural materials are required for establishing tasks, and require resources if produced 
-4. **Process:** Tasks/processes are set up by utilizing materials, and convert resources from on form to the other.
-5. **Location:** Locations are essentially a set of process. Capacity, cost, and demand variability can also be introduced.
-6. **Transport:** Modes of transportation translocate resources. Materials usage can also be provided.
-7. **Network:** Networks connect locations using transport linkages. The distance and transport availability matrices need to be provided. 
-8. **Scenario:** single location (using Location) or multi-location (using Network) scenarios can be generated at appropriate scales.
-
-Scenarios can then be formulated as a pyomo instance with the formulate function by using a set of constraints, setting an objective, providing demand targets, etc. Sets and variables are defined implicitly. Bespoke constraints can be added using the pyomo syntax. The solve functionality using appropriate solvers provides a solution (Result), which can be exported, analyzed, or itself used to initialize models. 
-
-## Select Features
-
-Available constraints are able to model:
-
-1. network design (using binaries)
-2. resource flows
-3. inventory balance
-4. costing
-5. emission (using global warming potential)
-6. land use
-7. mode based production (multiple resource inputs, nonlinear behavior modeling using piece-wise linear curves)
-8. transportation
-9. failure rates
-
-A model can be optimized towards:
-
-1. minimizing cost
-2. minimizing emission
-3. maximizing resource discharge
-
-Large scenarios can be aggregated using the following available techniques:
-
-1. agglomerative hierarchial clustering (AHC)
-2. dynamic time warping (DTW)
-
-Both the input data and solution output can be plotted with energiapy's own plot module:
-
-1. input data: capacity, demand, cost factors
-2. solution output: inventory, production, consumption, discharge/sales schedule; contribution towards costs (capital, variable and fixed operational), meeting demand.
-
-Other (and optional) features include:
-
-1. math utilities for calculating euclidean distances, generation connectivity matrices, etc.
-2. generating dataframes with missing data fixed (weekends, public holidays) for time-series data such as resource price
-3. function to fetch weather data at an appropriate resolution from [NREL NSRDB](https://nsrdb.nrel.gov/) for any county in the US
-4. latex constraints writer for model documentation
-
-
-
+# energiapy
+
+[![Documentation Status](https://readthedocs.org/projects/energiapy/badge/)](https://energiapy.readthedocs.io/en/latest/)
+[![PyPI](https://img.shields.io/pypi/v/energiapy.svg)](https://pypi.org/project/energiapy)
+[![Downloads](https://static.pepy.tech/personalized-badge/energiapy?period=total&units=international_system&left_color=grey&right_color=orange&left_text=Downloads)](https://pepy.tech/project/energiapy)
+
+
+energiapy is a tool for the multiscale modeling and optimization of energy systems. energiapy uses a resource task network (RTN) based approach to formulate mathematical programs as pyomo instances. Models can be solved using solvers, and the results can be analyzed within the energiapy framework. 
+Constituent functionalities have been built over the research conducted by the [Multiparameteric Optimization and Control (Pistikopoulos) Group](https://parametric.tamu.edu/).
+
+
+## Installation
+
+energiapy can be installed using the standard pip installation. It is recommended to install the package in a separate conda or pip environment. 
+
+Create and activate a conda environment 
+
+    conda create --name energiaenv python=3.10
+    conda activate energiaenv
+
+Or, create and activate a pip environment 
+
+    python3.10 -m venv energiaenv
+    .\energiaenv\Scripts\activate
+
+Install energiapy
+
+    pip install energiapy
+
+energiapy is being developed as a PhD project, and is hence frequently updated. For the most updated (sometimes unstable) version use
+
+    pip install git+https://github.com/TAMUparametric/energiapy
+
+## Key Applications
+
+1. Design of future energy systems (network design)
+2. Scheduling under uncertainty
+3. Life-cycle and environmental impact assessment
+4. Techno-economic analysis 
+5. System resiliency and reliability characterization
+
+Models can also simultaneously assuage the above under an integrated paradigm.
+
+## Modeling Approach
+
+Model components are declared as energiapy objects. The suggested hierarchy is as follows:
+
+1. **Temporal_scale:** The multiple temporal scales being considered
+2. **Resource:** Resources can be in any quantifiable form such mass, energy, information
+3. **Material:** Infrastructural materials are required for establishing tasks, and require resources if produced 
+4. **Process:** Tasks/processes are set up by utilizing materials, and convert resources from on form to the other.
+5. **Location:** Locations are essentially a set of process. Capacity, cost, and demand variability can also be introduced.
+6. **Transport:** Modes of transportation translocate resources. Materials usage can also be provided.
+7. **Network:** Networks connect locations using transport linkages. The distance and transport availability matrices need to be provided. 
+8. **Scenario:** single location (using Location) or multi-location (using Network) scenarios can be generated at appropriate scales.
+
+Scenarios can then be formulated as a pyomo instance with the formulate function by using a set of constraints, setting an objective, providing demand targets, etc. Sets and variables are defined implicitly. Bespoke constraints can be added using the pyomo syntax. The solve functionality using appropriate solvers provides a solution (Result), which can be exported, analyzed, or itself used to initialize models. 
+
+## Select Features
+
+Available constraints are able to model:
+
+1. network design (using binaries)
+2. resource flows
+3. inventory balance
+4. costing
+5. emission (using global warming potential)
+6. land use
+7. mode based production (multiple resource inputs, nonlinear behavior modeling using piece-wise linear curves)
+8. transportation
+9. failure rates
+
+A model can be optimized towards:
+
+1. minimizing cost
+2. minimizing emission
+3. maximizing resource discharge
+
+Large scenarios can be aggregated using the following available techniques:
+
+1. agglomerative hierarchial clustering (AHC)
+2. dynamic time warping (DTW)
+
+Both the input data and solution output can be plotted with energiapy's own plot module:
+
+1. input data: capacity, demand, cost factors
+2. solution output: inventory, production, consumption, discharge/sales schedule; contribution towards costs (capital, variable and fixed operational), meeting demand.
+
+Other (and optional) features include:
+
+1. math utilities for calculating euclidean distances, generation connectivity matrices, etc.
+2. generating dataframes with missing data fixed (weekends, public holidays) for time-series data such as resource price
+3. function to fetch weather data at an appropriate resolution from [NREL NSRDB](https://nsrdb.nrel.gov/) for any county in the US
+4. latex constraints writer for model documentation
+
+
+
diff --git a/conf.py b/conf.py
new file mode 100644
index 00000000..06d75283
--- /dev/null
+++ b/conf.py
@@ -0,0 +1,28 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# For the full list of built-in configuration values, see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Project information -----------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
+
+project = 'energiapy'
+copyright = '2023, Rahul Kakodkar'
+author = 'Rahul Kakodkar'
+release = '1.0.4'
+
+# -- General configuration ---------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
+
+extensions = []
+
+templates_path = ['_templates']
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+
+
+# -- Options for HTML output -------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
+
+html_theme = 'alabaster'
+html_static_path = []
diff --git a/docs/Makefile b/docs/Makefile
deleted file mode 100644
index d4bb2cbb..00000000
--- a/docs/Makefile
+++ /dev/null
@@ -1,20 +0,0 @@
-# Minimal makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line, and also
-# from the environment for the first two.
-SPHINXOPTS    ?=
-SPHINXBUILD   ?= sphinx-build
-SOURCEDIR     = .
-BUILDDIR      = _build
-
-# Put it first so that "make" without argument is like "make help".
-help:
-	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
-
-.PHONY: help Makefile
-
-# Catch-all target: route all unknown targets to Sphinx using the new
-# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
-%: Makefile
-	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/conf.py b/docs/conf.py
index 94cbcf9b..85958e91 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -22,7 +22,7 @@
 project = 'energiapy'
 copyright = '2023, Rahul Kakodkar'
 author = 'Rahul Kakodkar'
-release = '1.0.0'
+release = '1.0.4'
 
 # -- General configuration ---------------------------------------------------
 # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
@@ -38,5 +38,5 @@
 # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
 
 html_theme = 'sphinx_rtd_theme'
-html_static_path = ['_static']
+html_static_path = ['static_']
 source_suffix = [".rst", ".md"]
\ No newline at end of file
diff --git a/docs/example_mpLP.rst b/docs/example_mpLP.rst
index cf9db0f3..4371297b 100644
--- a/docs/example_mpLP.rst
+++ b/docs/example_mpLP.rst
@@ -104,7 +104,6 @@ Given is a general MILP modeling and optimization framework for simultaneous net
         Inv_{r,t} \leq Cap^{S}_{r,t}  \hspace{1cm} \forall r \in \mathcal{R}^{storage}, t \in \mathcal{T}^{sch}
     \end{equation} 
 
-
 .. math::
     \begin{equation}
         - S_{r,t} \leq - D_{r,t}  \hspace{1cm} \forall r \in \mathcal{R}, t \in \mathcal{T}^{sch}
@@ -144,7 +143,6 @@ Reformulated, a general mpLP for the above MILP will looks something like this:
         min \hspace{1cm} \sum_{p \in \mathcal{P}} Capex_p \times \epsilon_p \times P_p + \sum_{r \in \mathcal{R}^{cons}} C_r \times \gamma_r 
     \end{equation}
 
-
 .. math::
     \begin{equation}
         Inv_r \leq Cap^{S-max}_r \hspace{1cm} \forall r \in \mathcal{R}^{stored}
@@ -257,6 +255,12 @@ Let us now look at an example problem
     \end{equation}
 
 
+
+**Example energiapy implementation**
+
+Let us now look at an example problem
+
+
 Now we are ready to implement the above problem in energiapy
 
 **Import modules**
diff --git a/docs/example_small.rst b/docs/example_small.rst
index 1d276ee2..4aa8c944 100644
--- a/docs/example_small.rst
+++ b/docs/example_small.rst
@@ -81,7 +81,6 @@ The sets and variables used are stated here
 
 Given is a general MILP modeling and optimization framework for simultaneous network design and scheduling for a single location problem.
 
-
 .. math::
     \begin{equation}
         min \sum_{t \in \mathcal{T}^{net}} \sum_{p \in \mathcal{P}} Capex_{p,t} \times Cap^P_{p,t} + \sum_{t \in \mathcal{T}^{sch}} \sum_{r \in \mathcal{R}^{cons}}  Price_{r,t}  \times C_{r,t} + \sum_{t \in \mathcal{T}^{sch}} \sum_{p \in \mathcal{P}}  Vopex_{r,t} \times P_{r,t} 
diff --git a/docs/index.rst b/docs/index.rst
index f27c74ee..0e457edf 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,28 +1,34 @@
-.. energiapy documentation master file, created by
-   sphinx-quickstart on Wed Feb 15 12:55:59 2023.
+.. energiapy documentation master file, created by Rahul Kakodkar
+   sphinx-quickstart on Wed Mar 15 15:20:31 2023.
    You can adapt this file completely to your liking, but it should at least
    contain the root `toctree` directive.
 
+.. mdinclude:: ../README.md
+
 Welcome to energiapy's documentation!
 =====================================
 
-.. mdinclude:: ../README.md
-
+Tutorial
+--------
 
 .. toctree::
    :maxdepth: 2
-   :caption: Examples
+   :caption: Overview:
 
    example_small
    example_multiloc
    example_mpLP
    example_ahc
 
+
+API
+---
+
 .. toctree::
-   :maxdepth: 5
-   :caption: API
+    :maxdepth: 5
+    :caption: API
 
-   energiapy
+    energiapy
 
 Indices and tables
 ==================
@@ -30,3 +36,4 @@ Indices and tables
 * :ref:`genindex`
 * :ref:`modindex`
 * :ref:`search`
+
diff --git a/docs/make.bat b/docs/make.bat
deleted file mode 100644
index 954237b9..00000000
--- a/docs/make.bat
+++ /dev/null
@@ -1,35 +0,0 @@
-@ECHO OFF
-
-pushd %~dp0
-
-REM Command file for Sphinx documentation
-
-if "%SPHINXBUILD%" == "" (
-	set SPHINXBUILD=sphinx-build
-)
-set SOURCEDIR=.
-set BUILDDIR=_build
-
-%SPHINXBUILD% >NUL 2>NUL
-if errorlevel 9009 (
-	echo.
-	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
-	echo.installed, then set the SPHINXBUILD environment variable to point
-	echo.to the full path of the 'sphinx-build' executable. Alternatively you
-	echo.may add the Sphinx directory to PATH.
-	echo.
-	echo.If you don't have Sphinx installed, grab it from
-	echo.https://www.sphinx-doc.org/
-	exit /b 1
-)
-
-if "%1" == "" goto help
-
-%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
-goto end
-
-:help
-%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
-
-:end
-popd
diff --git a/index.rst b/index.rst
new file mode 100644
index 00000000..1811f292
--- /dev/null
+++ b/index.rst
@@ -0,0 +1,38 @@
+.. energiapy documentation master file, created by
+   sphinx-quickstart on Wed Mar 15 16:51:49 2023.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+
+.. mdinclude:: ../README.md
+
+Welcome to energiapy's documentation!
+=====================================
+
+Tutorial
+--------
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Overview:
+
+   example_small
+   example_multiloc
+   example_mpLP
+   example_ahc
+
+API
+---
+
+.. toctree::
+    :maxdepth: 5
+    :caption: API
+
+    energiapy
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/setup.py b/setup.py
index 2e35e0cc..281c0322 100644
--- a/setup.py
+++ b/setup.py
@@ -1,37 +1,43 @@
 from setuptools import setup, find_packages
-
-__version__ = "1.0.1"
+import os
+__version__ = "1.0.4"
 
 short_desc = (
     "Python-based energy systems modeling and optimization tool"
 )
 
-with open('README.md') as f:
-    long_description = f.read()
-
-setup(
-    name='energiapy',
-    version=__version__,
-    author='Rahul Kakodkar',
-    author_email='cacodcar@tamu.edu',
-    description=short_desc,
-    long_description=long_description,
-    long_description_content_type="text/markdown",
-    license='MIT',
-    url='https://github.com/TAMUparametric/energiapy',
-    install_requires=[
-        'numpy',
-        'pandas',
-        'pyomo',
-        'h5pyd',
-        'scipy',
-        'matplotlib',
-        'scikit-learn',
-        'openpyxl',
-        'ppopt',
-        'gurobipy',
-    ],
-    packages=find_packages(where='src'),
-    package_dir={'': 'src'}
-)
-
+def setup_package():
+    with open('README.md', encoding='utf-8') as f:
+            long_description = f.read()
+        
+    setup(
+        name='energiapy',
+        version=__version__,
+        author='Rahul Kakodkar',
+        author_email='cacodcar@tamu.edu',
+        description=short_desc,
+        long_description=long_description,
+        long_description_content_type="text/markdown",
+        license='MIT',
+        url='https://github.com/TAMUparametric/energiapy',
+        install_requires=[
+            'numpy',
+            'pandas',
+            'pyomo',
+            'h5pyd',
+            'scipy',
+            'matplotlib',
+            'scikit-learn',
+            'openpyxl',
+            'ppopt',
+            'gurobipy',
+        ],
+        extras_require={
+            'test': ['pytest'],
+        },
+        packages=find_packages(where='src'),
+        package_dir={'': 'src'},
+    )
+    
+if __name__ == '__main__':
+    setup_package()
\ No newline at end of file
diff --git a/src/__init__.py b/src/__init__.py
index d66ee6eb..e69de29b 100644
--- a/src/__init__.py
+++ b/src/__init__.py
@@ -1,11 +0,0 @@
-"""main init file for energiapy package
-"""
-
-__author__ = "Rahul Kakodkar"
-__copyright__ = "Copyright 2022, Multi-parametric Optimization & Control Lab"
-__credits__ = ["Rahul Kakodkar", "Efstratios N. Pistikopoulos"]
-__license__ = "Open"
-__version__ = "0.0.1"
-__maintainer__ = "Rahul Kakodkar"
-__email__ = "cacodcar@tamu.edu"
-__status__ = "Production"
diff --git a/src/energiapy/aggregation/__init__.py b/src/energiapy/aggregation/__init__.py
index 6e3ef566..e69de29b 100644
--- a/src/energiapy/aggregation/__init__.py
+++ b/src/energiapy/aggregation/__init__.py
@@ -1,11 +0,0 @@
-"""energiapy aggregation init file
-"""
-
-__author__ = "Rahul Kakodkar"
-__copyright__ = "Copyright 2023, Multi-parametric Optimization & Control Lab"
-__credits__ = ["Rahul Kakodkar", "Efstratios N. Pistikopoulos"]
-__license__ = "Open"
-__version__ = "0.0.1"
-__maintainer__ = "Rahul Kakodkar"
-__email__ = "cacodcar@tamu.edu"
-__status__ = "Production"
diff --git a/src/energiapy/aggregation/ahc.py b/src/energiapy/aggregation/ahc.py
index 341f71c1..7408efe9 100644
--- a/src/energiapy/aggregation/ahc.py
+++ b/src/energiapy/aggregation/ahc.py
@@ -5,7 +5,7 @@
 __copyright__ = "Copyright 2023, Multi-parametric Optimization & Control Lab"
 __credits__ = ["Rahul Kakodkar", "Efstratios N. Pistikopoulos"]
 __license__ = "Open"
-__version__ = "0.0.1"
+__version__ = "1.0.4"
 __maintainer__ = "Rahul Kakodkar"
 __email__ = "cacodcar@tamu.edu"
 __status__ = "Production"
@@ -225,21 +225,21 @@ def agg_hierarchial_elbow(scenario: Scenario, scale_level: int, include: list, r
         y_line = [theta[3] + theta[2] * pow(x, 1) + theta[1] * pow(x, 2) + theta[0] * pow(x, 3) for x in range_list]
         y_slope = [theta[2] + 2*theta[1] * pow(x, 1) + 3*theta[0] * pow(x, 2) for x in range_list]
 
-    #fig, ax = plt.subplots(figsize=(8, 6))
-    #x = range_list
+    # fig, ax = plt.subplots(figsize=(8, 6))
+    # x = range_list
     
     # ax.plot(x, y_line, label = 'MARS fit', color = 'steelblue', alpha = 0.6)
     
-    #ax.scatter(x, wcss_list, color = 'indianred')
+    # ax.scatter(x, wcss_list, color = 'indianred')
     # plt.axvline(x = elbow, alpha = 0.6, linestyle = 'dotted', label = f"elbow at {elbow}", color = 'slategrey', zorder = 3)    
 
-    #included = ''.join([str(i).split('IncludeAHC.')[1] + str(' ') for i in include])
+    # included = ''.join([str(i).split('IncludeAHC.')[1] + str(' ') for i in include])
     
     
-    #plt.title(f'Clustering using AHC for Houston for {included}')
-    #plt.xlabel('Cluster Size')
-    #plt.ylabel('WCSS')
-    #plt.grid(alpha=0.3)
+    # plt.title(f'Clustering using AHC for Houston for {included}')
+    # plt.xlabel('Cluster Size')
+    # plt.ylabel('WCSS')
+    # plt.grid(alpha=0.3)
 
     # plt.legend()
     
diff --git a/src/energiapy/components/location.py b/src/energiapy/components/location.py
index 0677dd33..399716d3 100644
--- a/src/energiapy/components/location.py
+++ b/src/energiapy/components/location.py
@@ -23,6 +23,7 @@
 from ..utils.scale_utils import scale_changer
 from pandas import DataFrame
 from enum import Enum, auto
+from warnings import warn
 
 
 
@@ -81,22 +82,29 @@ def __post_init__(self):
         self.materials = self.get_materials()
         self.scale_levels = self.scales.scale_levels
         self.processes_full = self.processes.union({create_storage_process(i) for i in self.processes if i.processmode == ProcessMode.storage})
+        self.prod_max = self.get_prod_max()
         
         if self.capacity_factor is not None:
             self.varying_capacity = set(self.capacity_factor.keys())
             if isinstance(list(self.capacity_factor.values())[0], DataFrame):
                 self.capacity_factor = scale_changer(self.capacity_factor, scales=self.scales, scale_level=self.capacity_scale_level)
-        
+            else:
+                warn('Input should be a dict of a DataFrame, Dict[Process, float]')
+                
         if self.cost_factor is not None:
             self.varying_cost = set(self.cost_factor.keys())
             if isinstance(list(self.cost_factor.values())[0], DataFrame):
                 self.cost_factor = scale_changer(self.cost_factor, scales=self.scales, scale_level=self.cost_scale_level)
-        
+            else:
+                warn('Input should be a dict of a DataFrame, Dict[Resource, float]')
+                
         if self.demand_factor is not None:    
             self.varying_demand = set(self.demand_factor.keys())
             if isinstance(list(self.demand_factor.values())[0], DataFrame):
                 self.demand_factor = scale_changer(self.demand_factor, scales=self.scales, scale_level=self.demand_scale_level)
-       
+            else:
+                warn('Input should be a dict of a DataFrame, Dict[Resource, float]')
+                
         self.resource_price = self.get_resource_price()   
         self.failure_processes = self.get_failure_processes()
         self.fail_factor = self.make_fail_factor()
@@ -113,7 +121,7 @@ def get_resources(self) -> Set[Resource]:
             resources_single = set().union(*[set(i.conversion.keys()) for i in self.processes if i.processmode == ProcessMode.single])
             resources_multi = set()
             for i in [i for i in self.processes if i.processmode == ProcessMode.multi]:
-                resources_multi  = resources_multi.union(*[set(j.keys()) for j in list(i.multiconversion.values())])
+                resources_multi  = resources_multi.union(*[set(j.keys()) for j in list(i.conversion.values())])
             return resources_single.union(resources_multi)
     
     def get_materials(self) -> Set[Material]:
@@ -157,7 +165,17 @@ def make_fail_factor(self)-> dict:
             scale_iter = [(i) for i in product(self.scales.scale[0], self.scales.scale[1], self.scales.scale[2])]
             fail_factor = {process_.name: {(scale_): sample([0]*int(process_.p_fail*100) +  [1] *int((1- process_.p_fail)*100), 1)[0] for scale_ in   scale_iter} for process_ in self.failure_processes}
             return fail_factor
-            
+        
+    def get_prod_max(self) -> dict:
+        prod_max_dict = dict()
+        for i in self.processes_full:
+            if i.processmode == ProcessMode.multi:
+                prod_max_dict[i.name] = i.prod_max
+            else:
+                prod_max_dict[i.name] = {0: None}
+                prod_max_dict[i.name][0] = i.prod_max
+        return prod_max_dict
+    
     def __repr__(self):
         return self.name
     
diff --git a/src/energiapy/components/process.py b/src/energiapy/components/process.py
index 9525f39d..89a15087 100644
--- a/src/energiapy/components/process.py
+++ b/src/energiapy/components/process.py
@@ -18,6 +18,8 @@
 import pandas
 from random import sample
 from enum import Enum, auto
+from warnings import warn
+
 
 class CostDynamics(Enum):
     constant = auto()
@@ -40,8 +42,7 @@ class Process:
 
     Args:
         name (str): name of process, short ones are better to deal with.
-        conversion (Dict[resource, float], optional): conversion data. Defaults to None.
-        multiconversion (Dict[int,Dict[Resource, float]], optional): Multiple modes for conversion can be passed as dictionaries. Defaults to None. 
+        conversion (Union[Dict[int,Dict[Resource, float]], Dict[Resource, float]], optional): conversion data (Dict[Resource, float]]), if multimode the of form Dict[int,Dict[Resource, float]]. Defaults to None.
         introduce (int, optional): Time period in the network scale when introduced. Defaults to 0.
         retire (int, optional): Time period in the network scale when retired. Defaults to None.
         capex (Union[float, dict], None): Capital expenditure per unit basis, can be scaled with capacity. Defaults to None.
@@ -72,25 +73,24 @@ class Process:
 
         For process with multiple modes of operation
         
-        >>> PEM = Process(name = 'PEM', multiconversion = {1: {Power: -1, H2: 19.474, O2: 763.2, H2O: -175.266}, 2: {Power: -1, H2: 1.2*19.474, O2: 1.2*763.2, H2O: 1.2*-175.266}, prod_max= 100, capex = 784000, label = 'PEM Electrolysis')
+        >>> PEM = Process(name = 'PEM', conversion = {1: {Power: -1, H2: 19.474, O2: 763.2, H2O: -175.266}, 2: {Power: -1, H2: 1.2*19.474, O2: 1.2*763.2, H2O: 1.2*-175.266}, prod_max= 100, capex = 784000, label = 'PEM Electrolysis')
         
         For process with multiple resource inputs
         
-        >>> CoolCar = Process(name = 'CoolCar', multiconversion = {1: {Power: -1, Mile: 1}, 2: {H2: -1, Mile:2}, prod_max= 50, capex = 70, label = 'CoolCar')
+        >>> CoolCar = Process(name = 'CoolCar', conversion = {1: {Power: -1, Mile: 1}, 2: {H2: -1, Mile:2}, prod_max= 50, capex = 70, label = 'CoolCar')
         
     """
 
     name: str 
     introduce: int = 0
     retire: int = None
-    conversion: Dict[Resource, float] = None
-    multiconversion: Dict[int,Dict[Resource, float]] = None 
+    conversion: Union[Dict[int,Dict[Resource, float]], Dict[Resource, float]] = None
     capex: Union[float, dict] = None
     fopex: Union[float, dict] = None
     vopex: Union[float, dict] = None
     incidental: float = None
     material_cons: Dict[Material, float] = None
-    prod_max: float = 0
+    prod_max: Union[Dict[int,float], float] = 0
     prod_min: float = 0
     basis: str = 'unit'
     carbon_credit: float = 0
@@ -119,11 +119,7 @@ def __post_init__(self):
             conversion_discharge (Dict[Resource, float]): Creates a dictionary with the discharge conversion values (considers storage loss).
             cost_dynamics (CostDynamics): Determines whether the cost scales linearly with the unit capacity, or is a piecewise-linear function.
         """
-        if self.multiconversion is not None:
-            self.processmode = ProcessMode.multi
-        elif self.conversion is not None:
-            self.processmode = ProcessMode.single
-            
+
         if self.storage is not None:
             self.resource_storage= create_storage_resource(process_name= self.name, resource= self.storage, store_max= self.store_max, store_min= self.store_min)
             self.conversion = {self.storage:-1, self.resource_storage:1}
@@ -133,12 +129,25 @@ def __post_init__(self):
         else:
             self.conversion_discharge = None
             self.resource_storage = None
-    
+            if type(list(self.conversion.keys())[0]) is int:
+                self.processmode = ProcessMode.multi
+            else:
+                self.processmode = ProcessMode.single
+                
         if type(self.capex) is dict:
             self.cost_dynamics = CostDynamics.pwl
         else:
             self.cost_dynamics = CostDynamics.constant      
-                    
+        
+        if self.processmode is ProcessMode.multi:
+            self.resource_req = {i.name for i in self.conversion[list(self.conversion.keys())[0]].keys()}
+        else:
+            self.resource_req = {i.name for i in self.conversion.keys()}     
+    
+        if self.processmode == ProcessMode.multi:    
+            if list(self.prod_max.keys()) != list(self.conversion.keys()):
+                warn('The keys for prod_max and conversion need to match if ProcessMode.multi')
+                            
     def __repr__(self):
         return self.name
     
diff --git a/src/energiapy/components/resource.py b/src/energiapy/components/resource.py
index 7e521145..8db97c02 100644
--- a/src/energiapy/components/resource.py
+++ b/src/energiapy/components/resource.py
@@ -14,6 +14,7 @@
 from dataclasses import dataclass
 import pandas
 from enum import Enum, auto
+from typing import Union
 
 class VaryingResource(Enum):
     deterministic_demand = auto()
@@ -38,7 +39,7 @@ class Resource:
         sell (bool, optional): True if resource can be discharged. Defaults to False.
         demand (bool, optional): True, if the process has to meet specific demand. If True, sell defaults to True. Defaults to False.
         basis (str, optional): Unit basis for the resource. Defaults to 'unit'.
-        block (str, optional): Assign a block for categorization. Defaults to None.
+        block (Union[str, list, dict], optional): Assign a block for categorization. Defaults to None.
         citation (str, optional): Add citations for data sources. Defaults to 'citation needed'.
         varying (bool, optional): If the cost of resource is varying/uncertain. Defaults to False.
         label (str, optional): Longer descriptive label if required. Defaults to ''.
@@ -70,7 +71,7 @@ class Resource:
     sell: bool = False
     demand: bool = False
     basis: str = 'unit'
-    block: str = ''
+    block: Union[str,list,dict] = ''
     citation: str = 'citation needed'
     varying: VaryingResource = None
     label: str = ''
diff --git a/src/energiapy/components/scenario.py b/src/energiapy/components/scenario.py
index 82cb3f7a..a13b5a95 100644
--- a/src/energiapy/components/scenario.py
+++ b/src/energiapy/components/scenario.py
@@ -145,10 +145,11 @@ def __post_init__(self):
         self.conversion = {i.name: {j.name: i.conversion[j] if j in i.conversion.keys() \
             else 0 for j in self.resource_set} for i in self.process_set if i.conversion is not None}
 
+        
         if type(self.demand) is dict:
             self.demand = {location.name: {resource.name: self.demand[location][resource] for resource in self.demand[location].keys()} for location in self.demand.keys()}
         
-        self.prod_max = {i.name: {j.name: j.prod_max for j in i.processes_full} for i in self.location_set}
+        self.prod_max = {i.name: i.prod_max for i in self.location_set}
         self.prod_min = {i.name: {j.name: j.prod_min for j in i.processes_full} for i in self.location_set}
         self.cons_max = {i.name: {j.name: j.cons_max for j in i.resources_full} for i in self.location_set}
         self.store_max = {i.name: {j.name: j.store_max for j in i.resources_full} for i in self.location_set}
@@ -170,11 +171,27 @@ def __post_init__(self):
         self.process_gwp_dict = {i.name: {j.name: j.gwp for j in self.process_set} for i in self.location_set}
         self.land_cost_dict = {i.name: i.land_cost for i in self.location_set}
         self.fail_factor = {i.name: i.fail_factor for i in self.location_set}
-        self.process_resource_dict = {i.name: {j.name for j in i.conversion.keys() } for i in self.process_set if i.conversion is not None}
+        
+        
+        self.process_resource_dict = {i.name: i.resource_req for i in self.process_set}
+        
         self.process_material_dict = {i.name: {j.name: i.material_cons[j] for j in i.material_cons.keys() } for i in self.process_set if i.material_cons is not None}
-        self.mode_dict = {i.name: [j for j in list(i.multiconversion.keys())] for i in self.process_set if i.processmode == ProcessMode.multi }
-    
 
+        multiconversion_dict = dict()
+        for i in self.process_set:
+            if i.processmode == ProcessMode.multi:
+                multiconversion_dict[i.name] = {j: None for j in i.conversion.keys()}
+                for k in list(multiconversion_dict[i.name].keys()):
+                    multiconversion_dict[i.name][k] = {j.name: i.conversion[k][j] if j in i.conversion[k].keys() else 0 for j in self.resource_set}
+            else:
+                multiconversion_dict[i.name] = {0: None}
+                multiconversion_dict[i.name][0] = {j.name: i.conversion[j]  if j in i.conversion.keys() else 0 for j in self.resource_set}
+        
+        self.multiconversion = multiconversion_dict
+                
+   
+        self.mode_dict = {i.name: [j for j in list(self.multiconversion[i.name].keys())] for i in self.process_set}
+    
         self.set_dict ={
             'resources': [i.name for i in self.resource_set],
             'resources_nosell': [i.name for i in self.resource_set if i.sell ==  False],
@@ -225,7 +242,7 @@ def __post_init__(self):
             self.set_dict['resources_trans'] = []
 
     def make_conversion_df(self):
-        return DataFrame.from_dict(self.conversion)
+        return DataFrame.from_dict(self.conversion).transpose()
     
         
     def matrix_form(self):
@@ -296,6 +313,7 @@ def matrix_form(self):
             
 
             #make A matrix
+            print(n_Inv, n_Sf, n_Cf, n_S, n_C)
             
             A_bal = numpy.diag([*[-1]*n_Inv, *[-1]*n_Sf, *[1]*n_Cf, *[-1]*n_S, *[1]*n_C])
 
@@ -304,9 +322,18 @@ def matrix_form(self):
             A_diag = numpy.diag([*[-1]*n_Inv, *[-1]*n_Sf, *[1]*n_Cf, *[1]*n_Pf, *[-1]*n_S, *[1]*n_C, *[1]*n_P])
 
             A_nn = numpy.eye(n_vars)
+            
+            print(A_bal)
+            
+            print(A_conv)
+            
+            print(A_diag)
+            
+            print(A_nn)
 
             A = numpy.block([ [numpy.block([A_bal, A_conv])], [A_diag],  [-A_nn]])
             
+        
             #make c matrix
             
             
diff --git a/src/energiapy/graph/__init__.py b/src/energiapy/graph/__init__.py
index 46d7931a..e69de29b 100644
--- a/src/energiapy/graph/__init__.py
+++ b/src/energiapy/graph/__init__.py
@@ -1,11 +0,0 @@
-"""energiapy.graph init file
-"""
-
-__author__ = "Rahul Kakodkar"
-__copyright__ = "Copyright 2022, Multi-parametric Optimization & Control Lab"
-__credits__ = ["Rahul Kakodkar", "Efstratios N. Pistikopoulos"]
-__license__ = "Open"
-__version__ = "0.0.1"
-__maintainer__ = "Rahul Kakodkar"
-__email__ = "cacodcar@tamu.edu"
-__status__ = "Production"
diff --git a/src/energiapy/model/__init__.py b/src/energiapy/model/__init__.py
index b257cd32..e69de29b 100644
--- a/src/energiapy/model/__init__.py
+++ b/src/energiapy/model/__init__.py
@@ -1,11 +0,0 @@
-"""energiapy.model init file
-"""
-
-__author__ = "Rahul Kakodkar"
-__copyright__ = "Copyright 2022, Multi-parametric Optimization & Control Lab"
-__credits__ = ["Rahul Kakodkar", "Efstratios N. Pistikopoulos"]
-__license__ = "Open"
-__version__ = "0.0.1"
-__maintainer__ = "Rahul Kakodkar"
-__email__ = "cacodcar@tamu.edu"
-__status__ = "Production"
diff --git a/src/energiapy/model/constraints/cost.py b/src/energiapy/model/constraints/cost.py
index ccd8bbcf..77c8c6a5 100644
--- a/src/energiapy/model/constraints/cost.py
+++ b/src/energiapy/model/constraints/cost.py
@@ -30,21 +30,43 @@ class Costdynamics(Enum):
     solar = auto()
 
 
-def transport_exp_cost_constraint(instance: ConcreteModel, scheduling_scale_level: int = 0, trans_cost: dict = {}, distance_dict: dict = {}) -> Constraint:
-    scales = scale_list(instance=instance,
-                        scale_levels=scheduling_scale_level+1)
-
-    def transport_exp_cost_rule(instance, source, sink, resource, transport, *scale_list):
-        return instance.Trans_exp_cost[source, sink, resource, transport, scale_list[:scheduling_scale_level+1]] == \
-            trans_cost[transport]*distance_dict[(source, sink)]*instance.Trans_exp[source,
-                                                                                   sink, resource, transport, scale_list[:scheduling_scale_level+1]]
-    instance.transport_exp_cost_constraint = Constraint(instance.sources, instance.sinks, instance.resources_trans,
-                                                        instance.transports, *scales, rule=transport_exp_cost_rule, doc='import of resource from sink to source')
-    constraint_latex_render(transport_exp_cost_rule)
-    return instance.transport_exp_cost_constraint
+# def constraint_transport_exp_cost(instance: ConcreteModel, scheduling_scale_level: int = 0, trans_cost: dict = {}, distance_dict: dict = {}) -> Constraint:
+#     """Calculates the expenditure on exporting resource throught transport
+
+#     Args:
+#         instance (ConcreteModel): pyomo instance
+#         scheduling_scale_level (int, optional): scheduling scale level. Defaults to 0.
+#         trans_cost (dict, optional): dictionary with transport costs. Defaults to {}.
+#         distance_dict (dict, optional): dictionary with distances. Defaults to {}.
+
+#     Returns:
+#         Constraint: transport_exp_cost
+#     """
+#     scales = scale_list(instance=instance,
+#                         scale_levels=scheduling_scale_level+1)
+
+#     def transport_exp_cost_rule(instance, source, sink, resource, transport, *scale_list):
+#         return instance.Trans_exp_cost[source, sink, resource, transport, scale_list[:scheduling_scale_level+1]] == \
+#             trans_cost[transport]*distance_dict[(source, sink)]*instance.Trans_exp[source,
+#                                                                                    sink, resource, transport, scale_list[:scheduling_scale_level+1]]
+#     instance.constraint_transport_exp_cost = Constraint(instance.sources, instance.sinks, instance.resources_trans,
+#                                                         instance.transports, *scales, rule=transport_exp_cost_rule, doc='import of resource from sink to source')
+#     constraint_latex_render(transport_exp_cost_rule)
+#     return instance.constraint_transport_exp_cost
+
+
+def constraint_transport_imp_cost(instance: ConcreteModel, scheduling_scale_level: int = 0, trans_cost: dict = {}, distance_dict: dict = {}) -> Constraint:    
+    """Calculates the expenditure on importing resource throught transport
 
+    Args:
+        instance (ConcreteModel): pyomo instance
+        scheduling_scale_level (int, optional): scheduling scale level. Defaults to 0.
+        trans_cost (dict, optional): dictionary with transport costs. Defaults to {}.
+        distance_dict (dict, optional): dictionary with distances. Defaults to {}.
 
-def transport_imp_cost_constraint(instance: ConcreteModel, scheduling_scale_level: int = 0, trans_cost: dict = {}, distance_dict: dict = {}) -> Constraint:
+    Returns:
+        Constraint: transport_imp_cost
+    """
     scales = scale_list(instance=instance,
                         scale_levels=scheduling_scale_level+1)
 
@@ -52,55 +74,71 @@ def transport_imp_cost_rule(instance, sink, source, resource, transport, *scale_
         return instance.Trans_imp_cost[sink, source, resource, transport, scale_list[:scheduling_scale_level+1]] == \
             trans_cost[transport]*distance_dict[(source, sink)]*instance.Trans_imp[sink,
                                                                                    source, resource, transport, scale_list[:scheduling_scale_level+1]]
-    instance.transport_imp_cost_constraint = Constraint(instance.sinks, instance.sources, instance.resources_trans,
+    instance.constraint_transport_imp_cost = Constraint(instance.sinks, instance.sources, instance.resources_trans,
                                                         instance.transports, *scales, rule=transport_imp_cost_rule, doc='import of resource from sink to source')
     constraint_latex_render(transport_imp_cost_rule)
-    return instance.transport_imp_cost_constraint
+    return instance.constraint_transport_imp_cost
 
 # *-------------------------Transport costing constraints--------------------------
 
 
-def transport_cost_constraint(instance: ConcreteModel, scheduling_scale_level: int = 0):
+def constraint_transport_cost(instance: ConcreteModel, scheduling_scale_level: int = 0) -> Constraint:
+    """Total cost for transport mode for all resources
+
+    Args:
+        instance (ConcreteModel): pyomo instance
+        scheduling_scale_level (int, optional): scale for scheduling decisions. Defaults to 0.
+
+    Returns:
+        Constraint: transport_cost
+    """
     scales = scale_list(instance=instance,
                         scale_levels=scheduling_scale_level+1)
 
     def transport_cost_rule(instance, transport, *scale_list):
         return instance.Trans_cost[transport, scale_list[:scheduling_scale_level+1]] == \
-            sum(instance.Trans_imp_cost[sink, source, resource, transport, scale_list[:scheduling_scale_level+1]] +
-                instance.Trans_exp_cost[source, sink, resource,
-                                        transport, scale_list[:scheduling_scale_level+1]]
+            sum(instance.Trans_imp_cost[sink, source, resource, transport, scale_list[:scheduling_scale_level+1]] 
                 for sink, source, resource in product(instance.sinks, instance.sources, instance.resources_trans))
-    instance.transport_cost_constraint = Constraint(
+    instance.constraint_transport_cost = Constraint(
         instance.transports, *scales, rule=transport_cost_rule, doc='total transport cost')
     constraint_latex_render(transport_cost_rule)
-    return instance.transport_cost_constraint
+    return instance.constraint_transport_cost
 
 
-def transport_cost_network_constraint(instance: ConcreteModel, network_scale_level: int = 0):
+def constraint_transport_cost_network(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
+    """Constraint for transport of all resources at network level
+
+    Args:
+        instance (ConcreteModel): pyomo instance
+        network_scale_level (int, optional): scale for network decisions. Defaults to 0.
+
+    Returns:
+        Constraint: transport_cost_network
+    """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
     scale_iter = scale_tuple(
         instance=instance, scale_levels=instance.scales.__len__())
 
     def transport_cost_network_rule(instance, transport, *scale_list):
         return instance.Trans_cost_network[transport, scale_list] == sum(instance.Trans_cost[transport, scale_] for scale_ in scale_iter)
-    instance.transport_cost_network_constraint = Constraint(
+    instance.constraint_transport_cost_network = Constraint(
         instance.transports, *scales, rule=transport_cost_network_rule, doc='total transport cost across scale')
     constraint_latex_render(transport_cost_network_rule)
-    return instance.transport_cost_network_constraint
+    return instance.constraint_transport_cost_network
 
 
-def process_capex_constraint(instance: ConcreteModel, capex_dict: dict, network_scale_level: int = 0, annualization_factor: float = 1,\
+def constraint_process_capex(instance: ConcreteModel, capex_dict: dict, network_scale_level: int = 0, annualization_factor: float = 1,\
     cost_dynamics: Costdynamics = Costdynamics.constant) -> Constraint:
     """Capital expenditure for each process at location in network
 
     Args:
         instance (ConcreteModel): pyomo instance
-        capex_dict (dict): capex at location #TODO
+        capex_dict (dict): capex at location 
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
         annualization_factor (float, optional): Annual depreciation of asset. Defaults to 1.
 
     Returns:
-        Constraint: process_capex_constraint
+        Constraint: process_capex
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
     
@@ -109,24 +147,24 @@ def process_capex_rule(instance, location, process, *scale_list):
             return instance.Capex_process[location, process, scale_list] == annualization_factor*capex_dict[process]*instance.Cap_P[location, process, scale_list]
         else:
             return instance.Capex_process[location, process, scale_list] == 0
-    instance.process_capex_constraint = Constraint(
+    instance.constraint_process_capex = Constraint(
         instance.locations, instance.processes, *scales, rule=process_capex_rule, doc='total purchase from network')
     
     constraint_latex_render(process_capex_rule)
-    return instance.process_capex_constraint
+    return instance.constraint_process_capex
 
-def process_incidental_constraint(instance: ConcreteModel, incidental_dict: dict, network_scale_level: int = 0, annualization_factor: float = 1,\
+def constraint_process_incidental(instance: ConcreteModel, incidental_dict: dict, network_scale_level: int = 0, annualization_factor: float = 1,\
     cost_dynamics: Costdynamics = Costdynamics.constant) -> Constraint:
     """Capital expenditure for each process at location in network
 
     Args:
         instance (ConcreteModel): pyomo instance
-        capex_dict (dict): capex at location #TODO
+        capex_dict (dict): capex at location
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
         annualization_factor (float, optional): Annual depreciation of asset. Defaults to 1.
 
     Returns:
-        Constraint: process_capex_constraint
+        Constraint: process_capex
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
     
@@ -135,23 +173,23 @@ def process_incidental_rule(instance, location, process, *scale_list):
             return instance.Incidental_process[location, process, scale_list] ==incidental_dict[process]
         else:
             return instance.Incidental_process[location, process, scale_list] == 0
-    instance.process_incidental_constraint = Constraint(
+    instance.constraint_process_incidental = Constraint(
         instance.locations, instance.processes, *scales, rule=process_incidental_rule, doc='total incidental costs from processes')
     
     constraint_latex_render(process_incidental_rule)
-    return instance.process_incidental_constraint
+    return instance.constraint_process_incidental
 
 
-def process_fopex_constraint(instance: ConcreteModel, fopex_dict: dict, network_scale_level: int = 0, annualization_factor: float = 1) -> Constraint:
+def constraint_process_fopex(instance: ConcreteModel, fopex_dict: dict, network_scale_level: int = 0, annualization_factor: float = 1) -> Constraint:
     """Fixed operational expenditure for each process at location in network
     Args:
         instance (ConcreteModel): pyomo instance
-        fopex_dict (dict): fixed opex at location #TODO
+        fopex_dict (dict): fixed opex at location
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
         annualization_factor (float, optional): Annual depreciation of asset. Defaults to 1.
 
     Returns:
-        Constraint: process_fopex_constraint
+        Constraint: process_fopex
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
@@ -160,13 +198,13 @@ def process_fopex_rule(instance, location, process, *scale_list):
             return instance.Fopex_process[location, process, scale_list] == annualization_factor*fopex_dict[process]*instance.Cap_P[location, process, scale_list]
         else:
             return instance.Fopex_process[location, process, scale_list] == 0 
-    instance.process_fopex_constraint = Constraint(
+    instance.constraint_process_fopex = Constraint(
         instance.locations, instance.processes, *scales, rule=process_fopex_rule, doc='total purchase from network')
     constraint_latex_render(process_fopex_rule)
-    return instance.process_fopex_constraint
+    return instance.constraint_process_fopex
 
 
-def process_vopex_constraint(instance: ConcreteModel, vopex_dict: dict, network_scale_level: int = 0, annualization_factor: float = 1) -> Constraint:
+def constraint_process_vopex(instance: ConcreteModel, vopex_dict: dict, network_scale_level: int = 0, annualization_factor: float = 1) -> Constraint:
     """Fixed operational expenditure for each process at location in network
 
     Args:
@@ -174,7 +212,7 @@ def process_vopex_constraint(instance: ConcreteModel, vopex_dict: dict, network_
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: process_vopex_constraint
+        Constraint: process_vopex
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
@@ -183,15 +221,15 @@ def process_vopex_rule(instance, location, process, *scale_list):
             return instance.Vopex_process[location, process, scale_list] == annualization_factor*vopex_dict[process]*instance.P_location[location, process, scale_list]
         else:
             return instance.Vopex_process[location, process, scale_list] == 0
-    instance.process_vopex_constraint = Constraint(
+    instance.constraint_process_vopex = Constraint(
         instance.locations, instance.processes, *scales, rule=process_vopex_rule, doc='total purchase from network')
     constraint_latex_render(process_vopex_rule)
-    return instance.process_vopex_constraint
+    return instance.constraint_process_vopex
 
 
 # *-------------------------Location costing constraints--------------------------------------
 
-def location_capex_constraint(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
+def constraint_location_capex(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
     """Capital expenditure for each process at location in network
 
     Args:
@@ -199,18 +237,18 @@ def location_capex_constraint(instance: ConcreteModel, network_scale_level: int
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: location_capex_constraint
+        Constraint: location_capex
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
     def location_capex_rule(instance, location, *scale_list):
         return instance.Capex_location[location, scale_list] == sum(instance.Capex_process[location, process_, scale_list] for process_ in instance.processes)
-    instance.location_capex_constraint = Constraint(
+    instance.constraint_location_capex = Constraint(
         instance.locations, *scales, rule=location_capex_rule, doc='total purchase from network')
     constraint_latex_render(location_capex_rule)
-    return instance.location_capex_constraint
+    return instance.constraint_location_capex
 
-def location_incidental_constraint(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
+def constraint_location_incidental(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
     """Capital expenditure for each process at location in network
 
     Args:
@@ -218,18 +256,18 @@ def location_incidental_constraint(instance: ConcreteModel, network_scale_level:
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: location_incidental_constraint
+        Constraint: location_incidental
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
     def location_incidental_rule(instance, location, *scale_list):
         return instance.Incidental_location[location, scale_list] == sum(instance.Incidental_process[location, process_, scale_list] for process_ in instance.processes)
-    instance.location_incidental_constraint = Constraint(
+    instance.constraint_location_incidental = Constraint(
         instance.locations, *scales, rule=location_incidental_rule, doc='total purchase from network')
     constraint_latex_render(location_incidental_rule)
-    return instance.location_incidental_constraint
+    return instance.constraint_location_incidental
 
-def location_fopex_constraint(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
+def constraint_location_fopex(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
     """Fixed operational expenditure for each process at location in network
 
     Args:
@@ -237,19 +275,19 @@ def location_fopex_constraint(instance: ConcreteModel, network_scale_level: int
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: location_fopex_constraint
+        Constraint: location_fopex
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
     def location_fopex_rule(instance, location, *scale_list):
         return instance.Fopex_location[location, scale_list] == sum(instance.Fopex_process[location, process_, scale_list] for process_ in instance.processes)
-    instance.location_fopex_constraint = Constraint(
+    instance.constraint_location_fopex = Constraint(
         instance.locations, *scales, rule=location_fopex_rule, doc='total purchase from network')
     constraint_latex_render(location_fopex_rule)
-    return instance.location_fopex_constraint
+    return instance.constraint_location_fopex
 
 
-def location_vopex_constraint(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
+def constraint_location_vopex(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
     """Fixed operational expenditure for each process at location in network
 
     Args:
@@ -257,21 +295,21 @@ def location_vopex_constraint(instance: ConcreteModel, network_scale_level: int
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: location_vopex_constraint
+        Constraint: location_vopex
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
     def location_vopex_rule(instance, location, *scale_list):
         return instance.Vopex_location[location, scale_list] == sum(instance.Vopex_process[location, process_, scale_list] for process_ in instance.processes)
-    instance.location_vopex_constraint = Constraint(
+    instance.constraint_location_vopex = Constraint(
         instance.locations, *scales, rule=location_vopex_rule, doc='total purchase from network')
     constraint_latex_render(location_vopex_rule)
-    return instance.location_vopex_constraint
+    return instance.constraint_location_vopex
 
 
 # *-------------------------Network costing constraints--------------------------------------
 
-def network_capex_constraint(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
+def constraint_network_capex(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
     """Capital expenditure for each process at location in network
 
     Args:
@@ -279,19 +317,19 @@ def network_capex_constraint(instance: ConcreteModel, network_scale_level: int =
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: network_capex_constraint
+        Constraint: network_capex
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
     def network_capex_rule(instance, *scale_list):
         return instance.Capex_network[scale_list] == sum(instance.Capex_location[location_, scale_list] for location_ in instance.locations)
-    instance.network_capex_constraint = Constraint(
+    instance.constraint_network_capex = Constraint(
         *scales, rule=network_capex_rule, doc='total purchase from network')
     constraint_latex_render(network_capex_rule)
-    return instance.network_capex_constraint
+    return instance.constraint_network_capex
 
 
-def network_incidental_constraint(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
+def constraint_network_incidental(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
     """Capital expenditure for each process at location in network
 
     Args:
@@ -299,19 +337,19 @@ def network_incidental_constraint(instance: ConcreteModel, network_scale_level:
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: network_incidental_constraint
+        Constraint: network_incidental
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
     def network_incidental_rule(instance, *scale_list):
         return instance.Incidental_network[scale_list] == sum(instance.Incidental_location[location_, scale_list] for location_ in instance.locations)
-    instance.network_incidental_constraint = Constraint(
+    instance.constraint_network_incidental = Constraint(
         *scales, rule=network_incidental_rule, doc='total purchase from network')
     constraint_latex_render(network_incidental_rule)
-    return instance.network_incidental_constraint
+    return instance.constraint_network_incidental
 
 
-def network_vopex_constraint(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
+def constraint_network_vopex(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
     """Variable operational expenditure for each process at location in network
 
     Args:
@@ -319,19 +357,19 @@ def network_vopex_constraint(instance: ConcreteModel, network_scale_level: int =
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: network_vopex_constraint
+        Constraint: network_vopex
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
     def network_vopex_rule(instance, *scale_list):
         return instance.Vopex_network[scale_list] == sum(instance.Vopex_location[location_, scale_list] for location_ in instance.locations)
-    instance.network_vopex_constraint = Constraint(
+    instance.constraint_network_vopex = Constraint(
         *scales, rule=network_vopex_rule, doc='total purchase from network')
     constraint_latex_render(network_vopex_rule)
-    return instance.network_vopex_constraint
+    return instance.constraint_network_vopex
 
 
-def network_fopex_constraint(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
+def constraint_network_fopex(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
     """Fixed operational for each process at location in network
 
     Args:
@@ -339,19 +377,19 @@ def network_fopex_constraint(instance: ConcreteModel, network_scale_level: int =
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: network_fopex_constraint
+        Constraint: network_fopex
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
     def network_fopex_rule(instance, *scale_list):
         return instance.Fopex_network[scale_list] == sum(instance.Fopex_location[location_, scale_list] for location_ in instance.locations)
-    instance.network_fopex_constraint = Constraint(
+    instance.constraint_network_fopex = Constraint(
         *scales, rule=network_fopex_rule, doc='total purchase from network')
     constraint_latex_render(network_fopex_rule)
-    return instance.network_fopex_constraint
+    return instance.constraint_network_fopex
 
 
-def process_land_cost_constraint(instance: ConcreteModel, land_dict: dict, land_cost_dict:dict, network_scale_level: int = 0) -> Constraint:
+def constraint_process_land_cost(instance: ConcreteModel, land_dict: dict, land_cost_dict:dict, network_scale_level: int = 0) -> Constraint:
     """Land cost for each process at location in network
 
     Args:
@@ -361,20 +399,20 @@ def process_land_cost_constraint(instance: ConcreteModel, land_dict: dict, land_
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: process_land_constraint
+        Constraint: process_land
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
     def process_land_cost_rule(instance, location, process, *scale_list):
         return instance.Land_cost_process[location, process, scale_list] == land_cost_dict[location]*land_dict[process]*instance.Cap_P[location, process, scale_list]
-    instance.process_land_cost_constraint = Constraint(
+    instance.constraint_process_land_cost = Constraint(
         instance.locations, instance.processes, *scales, rule=process_land_cost_rule, doc='land cost for process at location')
     constraint_latex_render(process_land_cost_rule)
-    return instance.process_land_cost_constraint
+    return instance.constraint_process_land_cost
 
 
 
-def location_land_cost_constraint(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
+def constraint_location_land_cost(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
     """Land cost each location in network
 
     Args:
@@ -382,20 +420,20 @@ def location_land_cost_constraint(instance: ConcreteModel, network_scale_level:
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: location_land_cost_constraint
+        Constraint: location_land_cost
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
     def location_land_cost_rule(instance, location, *scale_list):
         return instance.Land_cost_location[location, scale_list] == sum(instance.Land_cost_process[location, process_, scale_list] for process_ in instance.processes)
-    instance.location_land_cost_constraint = Constraint(
+    instance.constraint_location_land_cost = Constraint(
         instance.locations, *scales, rule=location_land_cost_rule, doc='land cost at location')
     constraint_latex_render(location_land_cost_rule)
-    return instance.location_land_cost_constraint
+    return instance.constraint_location_land_cost
 
 
 
-def network_land_cost_constraint(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
+def constraint_network_land_cost(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
     """Land cost by network
 
     Args:
@@ -403,13 +441,13 @@ def network_land_cost_constraint(instance: ConcreteModel, network_scale_level: i
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: network_land_cost_constraint
+        Constraint: network_land_cost
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
     def network_land_cost_rule(instance, *scale_list):
         return instance.Land_cost_network[scale_list] == sum(instance.Land_cost_location[location_, scale_list] for location_ in instance.locations)
-    instance.network_land_cost_constraint = Constraint(
+    instance.constraint_network_land_cost = Constraint(
         *scales, rule=network_land_cost_rule, doc='land cost for process')
     constraint_latex_render(network_land_cost_rule)
-    return instance.network_land_cost_constraint
\ No newline at end of file
+    return instance.constraint_network_land_cost
\ No newline at end of file
diff --git a/src/energiapy/model/constraints/emission.py b/src/energiapy/model/constraints/emission.py
index 6b31bb96..55e0fee7 100644
--- a/src/energiapy/model/constraints/emission.py
+++ b/src/energiapy/model/constraints/emission.py
@@ -22,7 +22,7 @@
 
 
 
-def carbon_emission_location_constraint(instance: ConcreteModel, network_scale_level:int=0) -> Constraint:
+def constraint_carbon_emission_location(instance: ConcreteModel, network_scale_level:int=0) -> Constraint:
     """Determines the total CO2_Vent at each location
 
     Args:
@@ -30,17 +30,17 @@ def carbon_emission_location_constraint(instance: ConcreteModel, network_scale_l
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: carbon_emission_location_constraint
+        Constraint: carbon_emission_location
     """
     scales = scale_list(instance= instance, scale_levels = network_scale_level+1)
     def carbon_emission_location_rule(instance, location, *scale_list):
         return instance.carbon_emission_location[location, scale_list] == instance.S_location[location, 'CO2_Vent', scale_list] 
-    instance.carbon_emission_location_constraint = Constraint(instance.locations, *scales, rule = carbon_emission_location_rule, doc = 'carbon_emission_location_process')
+    instance.constraint_carbon_emission_location = Constraint(instance.locations, *scales, rule = carbon_emission_location_rule, doc = 'carbon_emission_location_process')
     
     constraint_latex_render(carbon_emission_location_rule)
-    return instance.carbon_emission_location_constraint
+    return instance.constraint_carbon_emission_location
 
-def carbon_emission_constraint(instance: ConcreteModel, carbon_bound:float,  network_scale_level:int=0, carbon_reduction_percentage:float = 0.0) -> Constraint:
+def constraint_carbon_emission(instance: ConcreteModel, carbon_bound:float,  network_scale_level:int=0, carbon_reduction_percentage:float = 0.0) -> Constraint:
     """Determines the total network-wide CO2_Vent 
 
     Args:
@@ -50,48 +50,88 @@ def carbon_emission_constraint(instance: ConcreteModel, carbon_bound:float,  net
         carbon_reduction_percentage (float, optional): percentage reduction required. Defaults to 0.0.
 
     Returns:
-        Constraint: carbon_emission_constraint
+        Constraint: carbon_emission
     """
     scales = scale_list(instance= instance, scale_levels = network_scale_level+1)
     def carbon_emission_rule(instance, location, *scale_list):
         return instance.S_location[location, 'CO2_Vent', scale_list] <= (100.0 - carbon_reduction_percentage)*carbon_bound/100.0
-    instance.carbon_emission_constraint = Constraint(instance.locations, *scales, rule = carbon_emission_rule, doc = 'carbon_emission_process')
+    instance.constraint_carbon_emission = Constraint(instance.locations, *scales, rule = carbon_emission_rule, doc = 'carbon_emission_process')
     constraint_latex_render(carbon_emission_rule)
-    return instance.carbon_emission_constraint
+    return instance.constraint_carbon_emission
 
 
-def global_warming_potential_process_constraint(instance: ConcreteModel, process_gwp_dict: dict, network_scale_level:int=0):
+def constraint_global_warming_potential_process(instance: ConcreteModel, process_gwp_dict: dict, network_scale_level:int=0) -> Constraint:
+    """calculates global warming potential for each process
+
+    Args:
+        instance (ConcreteModel): pyomo model instance
+        process_gwp_dict (dict): _description_
+        network_scale_level (int, optional): scale for network decisions. Defaults to 0.
+
+    Returns:
+        Constraint: global_warming_potential_process
+    """
     scales = scale_list(instance= instance, scale_levels= network_scale_level+1)
     
     def global_warming_potential_process_rule(instance, location, process,  *scale_list):
         return instance.global_warming_potential_process[location, process, scale_list] == process_gwp_dict[location][process]*instance.Cap_P[location, process, scale_list]
-    instance.global_warming_potential_process_constraint = Constraint(instance.locations, instance.processes, *scales, rule = global_warming_potential_process_rule, doc= 'global warming potential for the each process' )
+    instance.constraint_global_warming_potential_process = Constraint(instance.locations, instance.processes, *scales, rule = global_warming_potential_process_rule, doc= 'global warming potential for the each process' )
     constraint_latex_render(global_warming_potential_process_rule)
-    return instance.global_warming_potential_process_constraint
+    return instance.constraint_global_warming_potential_process
+
+
+def constraint_global_warming_potential_material(instance: ConcreteModel, material_gwp_dict: dict, process_material_dict: dict, network_scale_level:int=0) -> Constraint:
+    """Calculates the global warming potential arising from the use of materials for processes
+
+    Args:
+        instance (ConcreteModel): pyomo model instance
+        material_gwp_dict (dict): GWP associated with each material 
+        process_material_dict (dict): Material consumed by each process
+        network_scale_level (int, optional): scale for network decisions. Defaults to 0.
 
-#TODO - Fix this, this needs some work
-def global_warming_potential_material_constraint(instance: ConcreteModel, material_gwp_dict: dict, process_material_dict: dict, network_scale_level:int=0):
+    Returns:
+        Constraint: global_warming_potential_material
+    """
     scales = scale_list(instance= instance, scale_levels= network_scale_level+1)
     
     def global_warming_potential_material_rule(instance, location, process,  *scale_list):
         return instance.global_warming_potential_material[location, process, scale_list] == \
             sum(process_material_dict[process][material]*material_gwp_dict[location][material] for material in process_material_dict[process].keys()) *\
                 instance.Cap_P[location, process, scale_list] 
-    instance.global_warming_potential_material_constraint = Constraint(instance.locations, instance.processes_materials, *scales, rule = global_warming_potential_material_rule, doc= 'global warming potential for the each material' )
+    instance.constraint_global_warming_potential_material = Constraint(instance.locations, instance.processes_materials, *scales, rule = global_warming_potential_material_rule, doc= 'global warming potential for the each material' )
     constraint_latex_render(global_warming_potential_material_rule)
-    return instance.global_warming_potential_material_constraint
+    return instance.constraint_global_warming_potential_material
+
+def constraint_global_warming_potential_resource(instance: ConcreteModel, resource_gwp_dict: dict, network_scale_level:int=0) -> Constraint:
+    """Calculates the global warming potential from each resource
+
+    Args:
+        instance (ConcreteModel): pyomo model instance
+        resource_gwp_dict (dict): GWP associated with each resource
+        network_scale_level (int, optional): scale for network decisions. Defaults to 0.
 
-def global_warming_potential_resource_constraint(instance: ConcreteModel, resource_gwp_dict: dict, network_scale_level:int=0):
+    Returns:
+        Constraint: global_warming_potential_resource
+    """
     scales = scale_list(instance= instance, scale_levels= network_scale_level+1)
     
     def global_warming_potential_resource_rule(instance, location, resource,  *scale_list):
         return instance.global_warming_potential_resource[location, resource, scale_list] == resource_gwp_dict[location][resource]*instance.C_location[location, resource, scale_list]
-    instance.global_warming_potential_resource_constraint = Constraint(instance.locations, instance.resources_purch, *scales, rule = global_warming_potential_resource_rule, doc= 'global warming potential for the each resource' )
+    instance.constraint_global_warming_potential_resource = Constraint(instance.locations, instance.resources_purch, *scales, rule = global_warming_potential_resource_rule, doc= 'global warming potential for the each resource' )
     constraint_latex_render(global_warming_potential_resource_rule)
-    return instance.global_warming_potential_resource_constraint
+    return instance.constraint_global_warming_potential_resource
 
 
-def global_warming_potential_location_constraint(instance: ConcreteModel, network_scale_level:int=0):
+def constraint_global_warming_potential_location(instance: ConcreteModel, network_scale_level:int=0) -> Constraint:
+    """Calculates the global warming potential at each location
+    
+    Args:
+        instance (ConcreteModel): pyomo model instance
+        network_scale_level (int, optional): scale for network decisions. Defaults to 0.
+
+    Returns:
+        Constraint: global_warming_potential_location
+    """
     scales = scale_list(instance= instance, scale_levels= network_scale_level+1)
     
     def global_warming_potential_location_rule(instance, location, *scale_list):
@@ -100,36 +140,48 @@ def global_warming_potential_location_rule(instance, location, *scale_list):
         gwp_material = sum(instance.global_warming_potential_material[location, material_, scale_list] for material_ in instance.processes_materials)
         
         return instance.global_warming_potential_location[location, scale_list] == gwp_process + gwp_resource + gwp_material
-    instance.global_warming_potential_location_constraint = Constraint(instance.locations, *scales, rule = global_warming_potential_location_rule, doc= 'global warming potential for the each location' )
+    instance.constraint_global_warming_potential_location = Constraint(instance.locations, *scales, rule = global_warming_potential_location_rule, doc= 'global warming potential for the each location' )
     constraint_latex_render(global_warming_potential_location_rule)
-    return instance.global_warming_potential_location_constraint
+    return instance.constraint_global_warming_potential_location
 
-def global_warming_potential_network_constraint(instance: ConcreteModel, network_scale_level:int=0):
+def constraint_global_warming_potential_network(instance: ConcreteModel, network_scale_level:int=0) -> Constraint:
+    """Global warming potential for the whole network
+
+    Args:
+        instance (ConcreteModel): pyomo model instance
+        network_scale_level (int, optional): scale for network decisions. Defaults to 0.
+
+    Returns:
+        Constraint: global_warming_potential_network
+    """
     scales = scale_list(instance= instance, scale_levels= network_scale_level+1)
     
     def global_warming_potential_network_rule(instance, *scale_list):
         return instance.global_warming_potential_network[scale_list] == \
             sum(instance.global_warming_potential_location[location_, scale_list] for location_ in instance.locations)
-    instance.global_warming_potential_network_constraint = Constraint(*scales, rule = global_warming_potential_network_rule, doc= 'global warming potential for the whole network' )
+    instance.constraint_global_warming_potential_network = Constraint(*scales, rule = global_warming_potential_network_rule, doc= 'global warming potential for the whole network' )
     constraint_latex_render(global_warming_potential_network_rule)
-    return instance.global_warming_potential_network_constraint
+    return instance.constraint_global_warming_potential_network
+
+def constraint_global_warming_potential_network_reduction(instance: ConcreteModel, network_scale_level:int=0, gwp_reduction_pct:float= 0, gwp:float = 0) -> Constraint:
+    """Required reduction in global warming potential at network level
+
+    Args:
+        instance (ConcreteModel): pyomo model instance
+        network_scale_level (int, optional): scale for network decisions. Defaults to 0.
+        gwp_reduction_pct (float, optional): GWP reduction required. Defaults to 0.
+        gwp (float, optional): Base Case (Current) GWP. Defaults to 0.
 
-def global_warming_potential_network_reduction_constraint(instance: ConcreteModel, network_scale_level:int=0, gwp_reduction_pct:float= 0, gwp:float = 0):
+    Returns:
+        Constraint: global_warming_potential_network_reduction
+    """
     scales = scale_list(instance= instance, scale_levels= network_scale_level+1)
     
     def global_warming_potential_network_reduction_rule(instance, *scale_list):
         return instance.global_warming_potential_network[scale_list] <= gwp*(1 - gwp_reduction_pct/100)
-    instance.global_warming_potential_network_reduction_constraint = Constraint(*scales, rule = global_warming_potential_network_reduction_rule, doc= 'global warming potential for the whole network' )
+    instance.constraint_global_warming_potential_network_reduction = Constraint(*scales, rule = global_warming_potential_network_reduction_rule, doc= 'global warming potential for the whole network' )
     constraint_latex_render(global_warming_potential_network_reduction_rule)
-    return instance.global_warming_potential_network_reduction_constraint
+    return instance.constraint_global_warming_potential_network_reduction
 
 
 
-def carbon_emission_network_constraint(instance: ConcreteModel, network_scale_level:int=0) -> Constraint:
-    scales = scale_list(instance= instance, scale_levels = network_scale_level+1)
-    def carbon_emission_network_rule(instance, *scale_list):
-        return instance.carbon_emission_network[scale_list] == sum(instance.carbon_emission_location[location_, scale_list] for location_ in instance.locations)
-    instance.carbon_emission_network_constraint = Constraint(*scales, rule = carbon_emission_network_rule, doc = 'carbon_emission_network_process')
-    constraint_latex_render(carbon_emission_network_rule)
-    return instance.carbon_emission_network_constraint
-
diff --git a/src/energiapy/model/constraints/failure.py b/src/energiapy/model/constraints/failure.py
index 46c6aade..76f085bb 100644
--- a/src/energiapy/model/constraints/failure.py
+++ b/src/energiapy/model/constraints/failure.py
@@ -21,7 +21,7 @@
 from enum import Enum, auto
 
 
-def nameplate_production_failure_constraint(instance: ConcreteModel, fail_factor:dict = {}, network_scale_level: int = 0, scheduling_scale_level: int = 0) -> Constraint:
+def constraint_nameplate_production_failure(instance: ConcreteModel, fail_factor:dict = {}, network_scale_level: int = 0, scheduling_scale_level: int = 0) -> Constraint:
     """Determines production capacity utilization of facilities at location in network and capacity of facilities 
 
     Args:
@@ -31,7 +31,7 @@ def nameplate_production_failure_constraint(instance: ConcreteModel, fail_factor
         scheduling_scale_level (int, optional): scale of scheduling decisions. Defaults to 0.
 
     Returns:
-        Constraint: nameplate_production_failure_constraint
+        Constraint: nameplate_production_failure
     """
     scales = scale_list(instance=instance,
                         scale_levels=instance.scales.__len__())
@@ -44,7 +44,7 @@ def nameplate_production_failure_rule(instance, location, process, *scale_list):
                                scale_list[:network_scale_level+1]]
         else:
             return instance.P[location, process, scale_list[:scheduling_scale_level+1]] <= instance.Cap_P[location, process, scale_list[:network_scale_level+1]]
-    instance.nameplate_production_failure_constraint = Constraint(
+    instance.constraint_nameplate_production_failure = Constraint(
         instance.locations, instance.processes, *scales, rule=nameplate_production_failure_rule, doc='nameplate production capacity constraint')
     constraint_latex_render(nameplate_production_failure_rule)
-    return instance.nameplate_production_failure_constraint
+    return instance.constraint_nameplate_production_failure
diff --git a/src/energiapy/model/constraints/inventory.py b/src/energiapy/model/constraints/inventory.py
index 62bb0d55..6f66a0a1 100644
--- a/src/energiapy/model/constraints/inventory.py
+++ b/src/energiapy/model/constraints/inventory.py
@@ -21,7 +21,7 @@
 from enum import Enum, auto
 
 
-def storage_facility_constraint(instance: ConcreteModel, store_max: dict, loc_res_dict: dict = {},  network_scale_level: int = 0) -> Constraint:
+def constraint_storage_facility(instance: ConcreteModel, store_max: dict, loc_res_dict: dict = {},  network_scale_level: int = 0) -> Constraint:
     """Determines where storage facility of certain capacity is inserted at location in network
 
     Args:
@@ -31,7 +31,7 @@ def storage_facility_constraint(instance: ConcreteModel, store_max: dict, loc_re
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: storage_facility_constraint
+        Constraint: storage_facility
     """
     scales = scale_list(instance=instance,
                         scale_levels=network_scale_level + 1)
@@ -43,12 +43,12 @@ def storage_facility_rule(instance, location, resource, *scale_list):
                              scale_list[:network_scale_level+1]]
         else:
             return instance.Cap_S[location, resource, scale_list[:network_scale_level+1]] == 0
-    instance.storage_facility_constraint = Constraint(
+    instance.constraint_storage_facility = Constraint(
         instance.locations, instance.resources_store, *scales, rule=storage_facility_rule, doc='storage facility sizing and location')
     constraint_latex_render(storage_facility_rule)
-    return instance.storage_facility_constraint
+    return instance.constraint_storage_facility
 
-def storage_facility_affix_constraint(instance: ConcreteModel, affix_storage_cap: dict, loc_res_dict: dict = {},  network_scale_level: int = 0) -> Constraint:
+def constraint_storage_facility_affix(instance: ConcreteModel, affix_storage_cap: dict, loc_res_dict: dict = {},  network_scale_level: int = 0) -> Constraint:
     """Determines where storage facility of certain capacity is inserted at location in network
 
     Args:
@@ -58,7 +58,7 @@ def storage_facility_affix_constraint(instance: ConcreteModel, affix_storage_cap
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: storage_facility_affix_constraint
+        Constraint: storage_facility_affix
     """
     scales = scale_list(instance=instance,
                         scale_levels=network_scale_level + 1)
@@ -72,13 +72,13 @@ def storage_facility_affix_rule(instance, location, resource, *scale_list):
                 return instance.Cap_S[location, resource, scale_list[:network_scale_level+1]] >= 0.0
         else:
             return instance.Cap_S[location, resource, scale_list[:network_scale_level+1]] == 0
-    instance.storage_facility_affix_constraint = Constraint(
+    instance.constraint_storage_facility_affix = Constraint(
         instance.locations, instance.resources_store, *scales, rule=storage_facility_affix_rule, doc='storage facility sizing and location')
     constraint_latex_render(storage_facility_affix_rule)
-    return instance.storage_facility_affix_constraint
+    return instance.constraint_storage_facility_affix
 
 
-def storage_facility_fix_constraint(instance: ConcreteModel, store_max: dict, storage_binaries: dict, loc_res_dict: dict = {},  network_scale_level: int = 0) -> Constraint:
+def constraint_storage_facility_fix(instance: ConcreteModel, store_max: dict, storage_binaries: dict, loc_res_dict: dict = {},  network_scale_level: int = 0) -> Constraint:
     """Determines where storage facility of certain capacity is inserted at location in network
 
     Args:
@@ -89,7 +89,7 @@ def storage_facility_fix_constraint(instance: ConcreteModel, store_max: dict, st
 
 
     Returns:
-        Constraint: storage_facility_fix_constraint
+        Constraint: storage_facility_fix
     """
     scales = scale_list(instance=instance,
                         scale_levels=network_scale_level + 1)
@@ -101,13 +101,13 @@ def storage_facility_fix_rule(instance, location, resource, *scale_list):
                                   scale_list[:network_scale_level+1])]
         else:
             return instance.Cap_S[location, resource, scale_list[:network_scale_level+1]] == 0
-    instance.storage_facility_fix_constraint = Constraint(
+    instance.constraint_storage_facility_fix = Constraint(
         instance.locations, instance.resources_store, *scales, rule=storage_facility_fix_rule, doc='storage facility sizing and location')
     constraint_latex_render(storage_facility_fix_rule)
-    return instance.storage_facility_fix_constraint
+    return instance.constraint_storage_facility_fix
 
 
-def min_storage_facility_constraint(instance: ConcreteModel, store_min: dict, loc_res_dict: dict = {},  network_scale_level: int = 0) -> Constraint:
+def constraint_min_storage_facility(instance: ConcreteModel, store_min: dict, loc_res_dict: dict = {},  network_scale_level: int = 0) -> Constraint:
     """Determines where storage facility of certain capacity is inserted at location in network
 
     Args:
@@ -118,7 +118,7 @@ def min_storage_facility_constraint(instance: ConcreteModel, store_min: dict, lo
 
 
     Returns:
-        Constraint: min_storage_facility_constraint
+        Constraint: min_storage_facility
     """
     scales = scale_list(instance=instance,
                         scale_levels=network_scale_level + 1)
@@ -130,13 +130,13 @@ def min_storage_facility_rule(instance, location, resource, *scale_list):
                              scale_list[:network_scale_level+1]]
         else:
             return Constraint.Skip
-    instance.min_storage_facility_constraint = Constraint(
+    instance.constraint_min_storage_facility = Constraint(
         instance.locations, instance.resources_store, *scales, rule=min_storage_facility_rule, doc='storage facility sizing and location')
     constraint_latex_render(min_storage_facility_rule)
-    return instance.min_storage_facility_constraint
+    return instance.constraint_min_storage_facility
 
 
-def nameplate_inventory_constraint(instance: ConcreteModel, loc_res_dict: dict = {}, network_scale_level: int = 0, scheduling_scale_level: int = 0) -> Constraint:
+def constraint_nameplate_inventory(instance: ConcreteModel, loc_res_dict: dict = {}, network_scale_level: int = 0, scheduling_scale_level: int = 0) -> Constraint:
     """Determines storage capacity utilization for resource at location in network and capacity of facilities 
 
     Args:
@@ -146,7 +146,7 @@ def nameplate_inventory_constraint(instance: ConcreteModel, loc_res_dict: dict =
         scheduling_scale_level (int, optional): scale of scheduling decisions. Defaults to 0.
 
     Returns:
-        Constraint: nameplate_inventory_constraint
+        Constraint: nameplate_inventory
     """
     scales = scale_list(instance=instance,
                         scale_levels=instance.scales.__len__())
@@ -156,7 +156,7 @@ def nameplate_inventory_rule(instance, location, resource, *scale_list):
             return instance.Inv[location, resource, scale_list[:scheduling_scale_level+1]] <= instance.Cap_S[location, resource, scale_list[:network_scale_level+1]]
         else:
             return instance.Inv[location, resource, scale_list[:scheduling_scale_level+1]] <= 0
-    instance.nameplate_inventory_constraint = Constraint(
+    instance.constraint_nameplate_inventory = Constraint(
         instance.locations, instance.resources_store, *scales, rule=nameplate_inventory_rule, doc='nameplate inventory capacity constraint')
     constraint_latex_render(nameplate_inventory_rule)
-    return instance.nameplate_inventory_constraint
+    return instance.constraint_nameplate_inventory
diff --git a/src/energiapy/model/constraints/land.py b/src/energiapy/model/constraints/land.py
index 69936262..c8e5f308 100644
--- a/src/energiapy/model/constraints/land.py
+++ b/src/energiapy/model/constraints/land.py
@@ -20,7 +20,7 @@
 from typing import Union
 from enum import Enum, auto
 
-def process_land_constraint(instance: ConcreteModel, land_dict: dict, network_scale_level: int = 0) -> Constraint:
+def constraint_process_land(instance: ConcreteModel, land_dict: dict, network_scale_level: int = 0) -> Constraint:
     """Land required for each process at location in network
 
     Args:
@@ -29,20 +29,20 @@ def process_land_constraint(instance: ConcreteModel, land_dict: dict, network_sc
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: process_land_constraint
+        Constraint: process_land
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
     def process_land_rule(instance, location, process, *scale_list):
         return instance.Land_process[location, process, scale_list] == land_dict[process]*instance.Cap_P[location, process, scale_list]
-    instance.process_land_constraint = Constraint(
+    instance.constraint_process_land = Constraint(
         instance.locations, instance.processes, *scales, rule=process_land_rule, doc='land required for process')
     constraint_latex_render(process_land_rule)
-    return instance.process_land_constraint
+    return instance.constraint_process_land
 
 
 
-def location_land_constraint(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
+def constraint_location_land(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
     """Land required at each location in network
 
     Args:
@@ -50,20 +50,20 @@ def location_land_constraint(instance: ConcreteModel, network_scale_level: int =
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: location_land_constraint
+        Constraint: location_land
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
     def location_land_rule(instance, location, *scale_list):
         return instance.Land_location[location, scale_list] == sum(instance.Land_process[location, process_, scale_list] for process_ in instance.processes)
-    instance.location_land_constraint = Constraint(
+    instance.constraint_location_land = Constraint(
         instance.locations, *scales, rule=location_land_rule, doc='land required for process')
     constraint_latex_render(location_land_rule)
-    return instance.location_land_constraint
+    return instance.constraint_location_land
 
 
 
-def network_land_constraint(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
+def constraint_network_land(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
     """Land required by network
 
     Args:
@@ -71,18 +71,18 @@ def network_land_constraint(instance: ConcreteModel, network_scale_level: int =
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: network_land_constraint
+        Constraint: network_land
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
     def network_land_rule(instance, *scale_list):
         return instance.Land_network[scale_list] == sum(instance.Land_location[location_, scale_list] for location_ in instance.locations)
-    instance.network_land_constraint = Constraint(
+    instance.constraint_network_land = Constraint(
         *scales, rule=network_land_rule, doc='land required for process')
     constraint_latex_render(network_land_rule)
-    return instance.network_land_constraint
+    return instance.constraint_network_land
 
-def location_land_restriction_constraint(instance: ConcreteModel, network_scale_level: int = 0, land_restriction: float = 0) -> Constraint:
+def constraint_location_land_restriction(instance: ConcreteModel, network_scale_level: int = 0, land_restriction: float = 0) -> Constraint:
     """Land required at each location in network
 
     Args:
@@ -90,13 +90,13 @@ def location_land_restriction_constraint(instance: ConcreteModel, network_scale_
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
  
     Returns:
-        Constraint: location_land_constraint
+        Constraint: location_land
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
     def location_land_restriction_rule(instance, location, *scale_list):
         return instance.Land_location[location, scale_list] <= land_restriction
-    instance.location_land_restriction_constraint = Constraint(
+    instance.constraint_location_land_restriction = Constraint(
         instance.locations, *scales, rule=location_land_restriction_rule, doc='land required for process')
     constraint_latex_render(location_land_restriction_rule)
-    return instance.location_land_restriction_constraint
\ No newline at end of file
+    return instance.constraint_location_land_restriction
\ No newline at end of file
diff --git a/src/energiapy/model/constraints/mode.py b/src/energiapy/model/constraints/mode.py
index ff6180d3..62132ae6 100644
--- a/src/energiapy/model/constraints/mode.py
+++ b/src/energiapy/model/constraints/mode.py
@@ -13,13 +13,74 @@
 from pyomo.environ import ConcreteModel, Constraint
 from ...utils.latex_utils import constraint_latex_render
 from ...utils.scale_utils import scale_list
-from ...utils.scale_utils import scale_pyomo_set
-from ...utils.scale_utils import scale_tuple
-from ...components.location import Location
-from itertools import product
-from typing import Union
-from enum import Enum, auto
 
+def constraint_production_mode(instance: ConcreteModel, mode_dict:dict, scheduling_scale_level: int = 0) -> Constraint:
+    """The sum of production through all modes equals production at scheduling scale
 
+    Args:
+        instance (ConcreteModel): pyomo model instance
+        mode_dict (dict): dictionary with modes available for process
+        scheduling_scale_level (int, optional): scale for scheduling decisions. Defaults to 0.
 
-# TODO Production mode constraints
+    Returns:
+        Constraint: production_mode
+    """
+
+    scales = scale_list(instance=instance, scale_levels=scheduling_scale_level+1)
+
+    def production_mode_rule(instance, location, process, *scale_list):
+        return instance.P[location, process, scale_list[:scheduling_scale_level+1]] == sum(instance.P_m[location, process, mode, scale_list[:scheduling_scale_level+1]] for mode in mode_dict[process])
+    instance.constraint_production_mode = Constraint(instance.locations, instance.processes, *scales, rule=production_mode_rule, doc='production mode sum constraint')
+    constraint_latex_render(production_mode_rule)
+    return instance.constraint_production_mode
+
+
+
+def constraint_production_mode_facility(instance: ConcreteModel, prod_max: dict, loc_pro_dict: dict = {}, scheduling_scale_level: int = 0) -> Constraint:
+    """Determines where production facility of certain capacity is mode for process at location in schedule
+
+    Args:
+        instance (ConcreteModel): pyomo instance
+        prod_max (dict): maximum production of process at location
+        loc_pro_dict (dict, optional): production facilities avaiable at location. Defaults to {}.
+        scheduling_scale_level (int, optional): scale of scheduling decisions. Defaults to 0.
+
+    Returns:
+        Constraint: production_facility_mode
+    """
+    scales = scale_list(instance=instance, scale_levels=scheduling_scale_level+1)
+
+    def production_mode_facility_rule(instance, location, process, mode, *scale_list):
+        if process in loc_pro_dict[location]:
+            if mode <= list(prod_max[location][process].keys())[-1:][0]:
+                return instance.Cap_P_m[location, process, mode, scale_list[:scheduling_scale_level+1]] <= prod_max[location][process][mode]*\
+                    instance.X_P_m[location, process, mode, scale_list[:scheduling_scale_level+1]]
+            else:
+                return Constraint.Skip
+        else:
+            return instance.Cap_P_m[location, process, mode, scale_list[:scheduling_scale_level+1]] == 0
+    instance.constraint_production_mode_facility = Constraint(
+        instance.locations, instance.processes, instance.modes, *scales, rule=production_mode_facility_rule, doc='production facility sizing and location')
+    constraint_latex_render(production_mode_facility_rule)
+    return instance.constraint_production_mode_facility
+
+def constraint_production_mode_binary(instance: ConcreteModel, mode_dict:dict, scheduling_scale_level: int = 0, network_scale_level: int = 0) -> Constraint:
+    """The sum of production through all modes equals production at scheduling scale
+
+    Args:
+        instance (ConcreteModel): pyomo model instance
+        mode_dict (dict): dictionary with modes available for process
+        scheduling_scale_level (int, optional): scale for scheduling decisions. Defaults to 0.
+        network_scale_level (int, optional): scale for network decisions. Defaults to 0.
+
+    Returns:
+        Constraint: production_mode_binary
+    """
+
+    scales = scale_list(instance=instance, scale_levels=scheduling_scale_level+1)
+
+    def production_mode_binary_rule(instance, location, process, *scale_list):
+        return instance.X_P[location, process, scale_list[:network_scale_level+1]] == sum(instance.X_P_m[location, process, mode, scale_list[:scheduling_scale_level+1]] for mode in mode_dict[process])
+    instance.constraint_production_mode_binary = Constraint(instance.locations, instance.processes, *scales, rule=production_mode_binary_rule, doc='production mode binary sum constraint')
+    constraint_latex_render(production_mode_binary_rule)
+    return instance.constraint_production_mode_binary
diff --git a/src/energiapy/model/constraints/production.py b/src/energiapy/model/constraints/production.py
index 67177f4a..5a47db4f 100644
--- a/src/energiapy/model/constraints/production.py
+++ b/src/energiapy/model/constraints/production.py
@@ -21,7 +21,7 @@
 from enum import Enum, auto
 
 
-def production_facility_constraint(instance: ConcreteModel, prod_max: dict, loc_pro_dict: dict = {}, network_scale_level: int = 0) -> Constraint:
+def constraint_production_facility(instance: ConcreteModel, prod_max: dict, loc_pro_dict: dict = {}, network_scale_level: int = 0) -> Constraint:
     """Determines where production facility of certain capacity is inserted at location in network
 
     Args:
@@ -31,24 +31,24 @@ def production_facility_constraint(instance: ConcreteModel, prod_max: dict, loc_
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: production_facility_constraint
+        Constraint: production_facility
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
     def production_facility_rule(instance, location, process, *scale_list):
         if process in loc_pro_dict[location]:
-            return instance.Cap_P[location, process, scale_list[:network_scale_level+1]] <= prod_max[location][process] *\
+            return instance.Cap_P[location, process, scale_list[:network_scale_level+1]] <= prod_max[location][process][list(prod_max[location][process].keys())[-1:][0]] *\
                 instance.X_P[location, process,
                              scale_list[:network_scale_level+1]]
         else:
             return instance.Cap_P[location, process, scale_list[:network_scale_level+1]] == 0
-    instance.production_facility_constraint = Constraint(
+    instance.constraint_production_facility = Constraint(
         instance.locations, instance.processes, *scales, rule=production_facility_rule, doc='production facility sizing and location')
     constraint_latex_render(production_facility_rule)
-    return instance.production_facility_constraint
+    return instance.constraint_production_facility
 
 
-def production_facility_affix_constraint(instance: ConcreteModel, affix_production_cap: dict, loc_pro_dict: dict = {}, network_scale_level: int = 0) -> Constraint:
+def constraint_production_facility_affix(instance: ConcreteModel, affix_production_cap: dict, loc_pro_dict: dict = {}, network_scale_level: int = 0) -> Constraint:
     """affixes the capacity of production facilities
 
     Args:
@@ -58,7 +58,7 @@ def production_facility_affix_constraint(instance: ConcreteModel, affix_producti
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: production_facility_affix_constraint
+        Constraint: production_facility_affix
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1) 
     def production_facility_affix_rule(instance, location, process, *scale_list):
@@ -70,13 +70,13 @@ def production_facility_affix_rule(instance, location, process, *scale_list):
                 return instance.Cap_P[location, process, scale_list[:network_scale_level+1]] >= 0.0
         else:
             return instance.Cap_P[location, process, scale_list[:network_scale_level+1]] == 0
-    instance.production_facility_affix_constraint = Constraint(
+    instance.constraint_production_facility_affix = Constraint(
         instance.locations, instance.processes, *scales, rule=production_facility_affix_rule, doc='production facility sizing and location')
     constraint_latex_render(production_facility_affix_rule)
-    return instance.production_facility_affix_constraint
+    return instance.constraint_production_facility_affix
 
 
-def production_facility_fix_constraint(instance: ConcreteModel, prod_max: dict, production_binaries: dict, loc_pro_dict: dict = {}, network_scale_level: int = 0) -> Constraint:
+def constraint_production_facility_fix(instance: ConcreteModel, prod_max: dict, production_binaries: dict, loc_pro_dict: dict = {}, network_scale_level: int = 0) -> Constraint:
     """Determines where production facility of certain capacity is inserted at location in network
 
     Args:
@@ -86,7 +86,7 @@ def production_facility_fix_constraint(instance: ConcreteModel, prod_max: dict,
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: production_facility_fix_constraint
+        Constraint: production_facility_fix
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
@@ -97,13 +97,13 @@ def production_facility_fix_rule(instance, location, process, *scale_list):
                     location, process, *scale_list[:network_scale_level+1])]
         else:
             return instance.Cap_P[location, process, scale_list[:network_scale_level+1]] == 0
-    instance.production_facility_fix_constraint = Constraint(
+    instance.constraint_production_facility_fix = Constraint(
         instance.locations, instance.processes, *scales, rule=production_facility_fix_rule, doc='production facility sizing and location')
     constraint_latex_render(production_facility_fix_rule)
-    return instance.production_facility_fix_constraint
+    return instance.constraint_production_facility_fix
 
 
-def min_production_facility_constraint(instance: ConcreteModel, prod_min: dict, loc_pro_dict: dict = {}, network_scale_level: int = 0) -> Constraint:
+def constraint_min_production_facility(instance: ConcreteModel, prod_min: dict, loc_pro_dict: dict = {}, network_scale_level: int = 0) -> Constraint:
     """Determines where production facility of certain capacity is inserted at location in network
 
     Args:
@@ -113,7 +113,7 @@ def min_production_facility_constraint(instance: ConcreteModel, prod_min: dict,
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: min_production_facility_constraint
+        Constraint: min_production_facility
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
@@ -124,12 +124,12 @@ def min_production_facility_rule(instance, location, process, *scale_list):
                              scale_list[:network_scale_level+1]]
         else:
             return Constraint.Skip
-    instance.min_production_facility_constraint = Constraint(
+    instance.constraint_min_production_facility = Constraint(
         instance.locations, instance.processes, *scales, rule=min_production_facility_rule, doc='production facility sizing and location')
     constraint_latex_render(min_production_facility_rule)
-    return instance.min_production_facility_constraint
+    return instance.constraint_min_production_facility
 
-def nameplate_production_constraint(instance: ConcreteModel, capacity_factor: dict = {}, loc_pro_dict: dict = {}, network_scale_level: int = 0, scheduling_scale_level: int = 0) -> Constraint:
+def constraint_nameplate_production(instance: ConcreteModel, capacity_factor: dict = {}, loc_pro_dict: dict = {}, network_scale_level: int = 0, scheduling_scale_level: int = 0) -> Constraint:
     """Determines production capacity utilization of facilities at location in network and capacity of facilities 
 
     Args:
@@ -140,7 +140,7 @@ def nameplate_production_constraint(instance: ConcreteModel, capacity_factor: di
         scheduling_scale_level (int, optional): scale of scheduling decisions. Defaults to 0.
 
     Returns:
-        Constraint: nameplate_production_constraint
+        Constraint: nameplate_production
     """
     scales = scale_list(instance=instance,
                         scale_levels=instance.scales.__len__())
@@ -157,8 +157,8 @@ def nameplate_production_rule(instance, location, process, *scale_list):
         else:
             return Constraint.Skip
         
-    instance.nameplate_production_constraint = Constraint(
+    instance.constraint_nameplate_production = Constraint(
         instance.locations, instance.processes, *scales, rule=nameplate_production_rule, doc='nameplate production capacity constraint')
     constraint_latex_render(nameplate_production_rule)
-    return instance.nameplate_production_constraint
+    return instance.constraint_nameplate_production
 
diff --git a/src/energiapy/model/constraints/resource_balance.py b/src/energiapy/model/constraints/resource_balance.py
index 7da79e71..1ebe6498 100644
--- a/src/energiapy/model/constraints/resource_balance.py
+++ b/src/energiapy/model/constraints/resource_balance.py
@@ -19,13 +19,11 @@
 from itertools import product
 from typing import Union
 from enum import Enum, auto
+from ...components.process import ProcessMode
 
-class ProcessMode(Enum):
-    single = auto() #only allows one mode
-    multi = auto() # allows multiple modes
 
 
-def resource_consumption_constraint(instance: ConcreteModel, loc_res_dict: dict = {}, cons_max: dict = {}, scheduling_scale_level: int = 0) -> Constraint:
+def constraint_resource_consumption(instance: ConcreteModel, loc_res_dict: dict = {}, cons_max: dict = {}, scheduling_scale_level: int = 0) -> Constraint:
     """Determines consumption of resource at location in network 
 
     Args:
@@ -35,7 +33,7 @@ def resource_consumption_constraint(instance: ConcreteModel, loc_res_dict: dict
         scheduling_scale_level (int, optional): scale of scheduling decisions. Defaults to 0.
 
     Returns:
-        Constraint: resource_consumption_constraint
+        Constraint: resource_consumption
     """
     scales = scale_list(instance=instance,
                         scale_levels=instance.scales.__len__())
@@ -45,13 +43,13 @@ def resource_consumption_rule(instance, location, resource, *scale_list):
             return instance.C[location, resource, scale_list[:scheduling_scale_level+1]] <= cons_max[location][resource]
         else:
             return instance.C[location, resource, scale_list[:scheduling_scale_level+1]] <= 0
-    instance.resource_consumption_constraint = Constraint(
+    instance.constraint_resource_consumption = Constraint(
         instance.locations, instance.resources_purch, *scales, rule=resource_consumption_rule, doc='resource consumption')
     constraint_latex_render(resource_consumption_rule)
-    return instance.resource_consumption_constraint
+    return instance.constraint_resource_consumption
 
 
-def resource_purchase_constraint(instance: ConcreteModel, cost_factor: dict = {}, price: dict = {},
+def constraint_resource_purchase(instance: ConcreteModel, cost_factor: dict = {}, price: dict = {},
                                  loc_res_dict: dict = {}, scheduling_scale_level: int = 0, expenditure_scale_level: int = 0) -> Constraint:
     """Determines expenditure on resource at location in network at the scheduling/expenditure scale
 
@@ -63,7 +61,7 @@ def resource_purchase_constraint(instance: ConcreteModel, cost_factor: dict = {}
         expenditure_scale_level (int, optional): scale of resource purchase decisions. Defaults to 0.
 
     Returns:
-        Constraint: resource_purchase_constraint
+        Constraint: resource_purchase
     """
     scales = scale_list(instance=instance,
                         scale_levels=instance.scales.__len__())
@@ -79,14 +77,14 @@ def resource_purchase_rule(instance, location, resource, *scale_list):
                 return instance.B[location, resource, scale_list[:scheduling_scale_level+1]] == price[location][resource]*instance.C[location, resource, scale_list[:scheduling_scale_level+1]]
             else:
                 return instance.B[location, resource, scale_list[:scheduling_scale_level+1]] == 0
-    instance.resource_purchase_constraint = Constraint(
+    instance.constraint_resource_purchase = Constraint(
         instance.locations, instance.resources_purch, *scales, rule=resource_purchase_rule, doc='expenditure on purchase of resource')
     constraint_latex_render(resource_purchase_rule)
-    return instance.resource_purchase_constraint
+    return instance.constraint_resource_purchase
 
 
-def inventory_balance_constraint(instance: ConcreteModel, scheduling_scale_level: int = 0, \
-    conversion: dict = {}, cluster_wt: dict = None) -> Constraint:
+def constraint_inventory_balance(instance: ConcreteModel, scheduling_scale_level: int = 0, \
+    multiconversion: dict = {}, mode_dict: dict = {}, cluster_wt: dict = None) -> Constraint:
     """balances resource across the scheduling horizon
     Mass balance in any temporal discretization has the following within their respective sets:
     - consumption for resources that can be purchased
@@ -102,10 +100,12 @@ def inventory_balance_constraint(instance: ConcreteModel, scheduling_scale_level
     Args:
         instance (ConcreteModel): pyomo instance
         scheduling_scale_level (int, optional): scale of scheduling decisions. Defaults to 0.
-        conversion (dict, optional): unit conversion of resource by production facility. Defaults to {}.
-
+        multiconversion (dict, optional): unit conversion of resource by production facility. Defaults to {}.
+        mode_dict (dict, optional): dictionary with modes available. Defaults to {}.
+        cluster_wt (dict, optional): weight of cluster as determined through scenario aggregation. Defaults to None. 
+    
     Returns:
-        Constraint: inventory_balance_constraint
+        Constraint: inventory_balance
     """
     scales = scale_list(instance=instance,
                         scale_levels=instance.scales.__len__())
@@ -149,20 +149,20 @@ def inventory_balance_rule(instance, location, resource, *scale_list):
         # produced = sum(conversion[process][resource]*instance.P[location, process, scale_list[:scheduling_scale_level+1]] for process in instance.processes_singlem) \
         #     + sum(instance.P[location, process, scale_list[:scheduling_scale_level+1]] for process in instance.processes_multim)
         
-        produced = sum(conversion[process][resource]*instance.P[location, process,
-                       scale_list[:scheduling_scale_level+1]] for process in instance.processes_full) #includes processes + discharge
-
+        produced = sum(sum(multiconversion[process][mode][resource]*instance.P_m[location, process, mode,
+                       scale_list[:scheduling_scale_level+1]] for mode in mode_dict[process]) for process in instance.processes_full) #includes processes + discharge
+    
         if cluster_wt is not None:
             return cluster_wt[scale_list[:scheduling_scale_level+1]]*(consumption + produced - discharge + transport) == storage
         else:
             return consumption + produced - discharge + transport == storage
-    instance.inventory_balance_constraint = Constraint(
+    instance.constraint_inventory_balance = Constraint(
         instance.locations, instance.resources, *scales, rule=inventory_balance_rule, doc='mass balance across scheduling scale')
-    constraint_latex_render(inventory_balance_constraint)
-    return instance.inventory_balance_constraint
+    constraint_latex_render(inventory_balance_rule)
+    return instance.constraint_inventory_balance
 
 
-def demand_constraint(instance: ConcreteModel, demand: Union[dict, float], demand_factor: Union[dict, float], \
+def constraint_demand(instance: ConcreteModel, demand: Union[dict, float], demand_factor: Union[dict, float], \
     demand_scale_level: int = 0, scheduling_scale_level: int = 0, cluster_wt: dict = None) -> Constraint:
     """Ensures that demand for resource is met at chosen temporal scale
 
@@ -173,7 +173,7 @@ def demand_constraint(instance: ConcreteModel, demand: Union[dict, float], deman
         demand_dict (dict, optional): demand at location. Defaults to {}.
 
     Returns:
-        Constraint: demand_constraint
+        Constraint: demand
     """
     # scales = scale_list(instance= instance, scale_levels = demand_scale_level+1)
     scales = scale_list(instance=instance,
@@ -216,17 +216,17 @@ def demand_rule(instance, location, resource, *scale_list):
         return discharge >= demandtarget
 
     if len(instance.locations) > 1:
-        instance.demand_constraint = Constraint(
+        instance.constraint_demand = Constraint(
             instance.sinks, instance.resources_demand, *scales, rule=demand_rule, doc='specific demand for resources')
     else:
-        instance.demand_constraint = Constraint(
+        instance.constraint_demand = Constraint(
             instance.locations, instance.resources_demand, *scales, rule=demand_rule, doc='specific demand for resources')
     constraint_latex_render(demand_rule)
-    return instance.demand_constraint
+    return instance.constraint_demand
 
 
 
-def location_production_constraint(instance: ConcreteModel, cluster_wt: dict, network_scale_level: int = 0) -> Constraint:
+def constraint_location_production(instance: ConcreteModel, cluster_wt: dict, network_scale_level: int = 0) -> Constraint:
     """Determines total production capacity utilization at location
 
     Args:
@@ -234,7 +234,7 @@ def location_production_constraint(instance: ConcreteModel, cluster_wt: dict, ne
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: location_production_constraint
+        Constraint: location_production
     """
 
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
@@ -246,13 +246,13 @@ def location_production_rule(instance, location, process, *scale_list):
             return instance.P_location[location, process, scale_list] == sum(cluster_wt[scale_]*instance.P[location, process, scale_] for scale_ in scale_iter)
         else:
             return instance.P_location[location, process, scale_list] == sum(instance.P[location, process, scale_] for scale_ in scale_iter)
-    instance.location_production_constraint = Constraint(
+    instance.constraint_location_production = Constraint(
         instance.locations, instance.processes, *scales, rule=location_production_rule, doc='total production at location')
     constraint_latex_render(location_production_rule)
-    return instance.location_production_constraint
+    return instance.constraint_location_production
 
 
-def location_discharge_constraint(instance: ConcreteModel, cluster_wt: dict, network_scale_level: int = 0) -> Constraint:
+def constraint_location_discharge(instance: ConcreteModel, cluster_wt: dict, network_scale_level: int = 0) -> Constraint:
     """Determines total resource discharged/sold at locations in network
 
     Args:
@@ -260,7 +260,7 @@ def location_discharge_constraint(instance: ConcreteModel, cluster_wt: dict, net
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: location_discharge_constraint
+        Constraint: location_discharge
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
     scale_iter = scale_tuple(
@@ -271,13 +271,13 @@ def location_discharge_rule(instance, location, resource, *scale_list):
             return instance.S_location[location, resource, scale_list] == sum(cluster_wt[scale_]*instance.S[location, resource, scale_] for scale_ in scale_iter)
         else:
             return instance.S_location[location, resource, scale_list] == sum(instance.S[location, resource, scale_] for scale_ in scale_iter)
-    instance.location_discharge_constraint = Constraint(
+    instance.constraint_location_discharge = Constraint(
         instance.locations, instance.resources_sell, *scales, rule=location_discharge_rule, doc='total discharge at location')
     constraint_latex_render(location_discharge_rule)
-    return instance.location_discharge_constraint
+    return instance.constraint_location_discharge
 
 
-def location_consumption_constraint(instance: ConcreteModel, cluster_wt: dict, network_scale_level: int = 0) -> Constraint:
+def constraint_location_consumption(instance: ConcreteModel, cluster_wt: dict, network_scale_level: int = 0) -> Constraint:
     """Determines total resource consumed at locations in network
 
     Args:
@@ -285,7 +285,7 @@ def location_consumption_constraint(instance: ConcreteModel, cluster_wt: dict, n
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: location_consumption_constraint
+        Constraint: location_consumption
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
     scale_iter = scale_tuple(
@@ -296,13 +296,13 @@ def location_consumption_rule(instance, location, resource, *scale_list):
             return instance.C_location[location, resource, scale_list] == sum(cluster_wt[scale_]*instance.C[location, resource, scale_] for scale_ in scale_iter)
         else:
             return instance.C_location[location, resource, scale_list] == sum(instance.C[location, resource, scale_] for scale_ in scale_iter)
-    instance.location_consumption_constraint = Constraint(
+    instance.constraint_location_consumption = Constraint(
         instance.locations, instance.resources_purch, *scales, rule=location_consumption_rule, doc='total consumption at location')
     constraint_latex_render(location_consumption_rule)
-    return instance.location_consumption_constraint
+    return instance.constraint_location_consumption
 
 
-def location_purchase_constraint(instance: ConcreteModel, cluster_wt: dict, network_scale_level: int = 0) -> Constraint:
+def constraint_location_purchase(instance: ConcreteModel, cluster_wt: dict, network_scale_level: int = 0) -> Constraint:
     """Determines total resource purchase expenditure at locations in network
 
     Args:
@@ -310,7 +310,7 @@ def location_purchase_constraint(instance: ConcreteModel, cluster_wt: dict, netw
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: location_purchase_constraint
+        Constraint: location_purchase
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
     scale_iter = scale_tuple(
@@ -321,15 +321,15 @@ def location_purchase_rule(instance, location, resource, *scale_list):
             return instance.B_location[location, resource, scale_list] == sum(cluster_wt[scale_]*instance.B[location, resource, scale_] for scale_ in scale_iter)
         else:
             return instance.B_location[location, resource, scale_list] == sum(instance.B[location, resource, scale_] for scale_ in scale_iter)
-    instance.location_purchase_constraint = Constraint(
+    instance.constraint_location_purchase = Constraint(
         instance.locations, instance.resources_purch, *scales, rule=location_purchase_rule, doc='total purchase at location')
     constraint_latex_render(location_purchase_rule)
-    return instance.location_purchase_constraint
+    return instance.constraint_location_purchase
 
 
 # *-------------------------Network scale mass balance calculation constraints--------------------------
 
-def network_production_constraint(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
+def constraint_network_production(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
     """Determines total production utilization across network
 
     Args:
@@ -337,19 +337,19 @@ def network_production_constraint(instance: ConcreteModel, network_scale_level:
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: network_production_constraint
+        Constraint: network_production
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
     def network_production_rule(instance, process, *scale_list):
         return instance.P_network[process, scale_list] == sum(instance.P_location[location_, process, scale_list] for location_ in instance.locations)
-    instance.network_production_constraint = Constraint(
+    instance.constraint_network_production = Constraint(
         instance.processes, *scales, rule=network_production_rule, doc='total production from network')
     constraint_latex_render(network_production_rule)
-    return instance.network_production_constraint
+    return instance.constraint_network_production
 
 
-def network_discharge_constraint(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
+def constraint_network_discharge(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
     """Determines total resource discharged across network
 
     Args:
@@ -357,19 +357,19 @@ def network_discharge_constraint(instance: ConcreteModel, network_scale_level: i
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: network_discharge_constraint
+        Constraint: network_discharge
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
     def network_discharge_rule(instance, resource, *scale_list):
         return instance.S_network[resource, scale_list] == sum(instance.S_location[location_, resource, scale_list] for location_ in instance.locations)
-    instance.network_discharge_constraint = Constraint(
+    instance.constraint_network_discharge = Constraint(
         instance.resources_sell, *scales, rule=network_discharge_rule, doc='total discharge from network')
     constraint_latex_render(network_discharge_rule)
-    return instance.network_discharge_constraint
+    return instance.constraint_network_discharge
 
 
-def network_consumption_constraint(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
+def constraint_network_consumption(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
     """Determines total resource consumed across network
 
     Args:
@@ -377,19 +377,19 @@ def network_consumption_constraint(instance: ConcreteModel, network_scale_level:
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: network_consumption_constraint
+        Constraint: network_consumption
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
     def network_consumption_rule(instance, resource, *scale_list):
         return instance.C_network[resource, scale_list] == sum(instance.C_location[location_, resource, scale_list] for location_ in instance.locations)
-    instance.network_consumption_constraint = Constraint(
+    instance.constraint_network_consumption = Constraint(
         instance.resources_purch, *scales, rule=network_consumption_rule, doc='total consumption from network')
     constraint_latex_render(network_consumption_rule)
-    return instance.network_consumption_constraint
+    return instance.constraint_network_consumption
 
 
-def network_purchase_constraint(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
+def constraint_network_purchase(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
     """Determines total purchase expenditure on resource across network
 
     Args:
@@ -397,13 +397,13 @@ def network_purchase_constraint(instance: ConcreteModel, network_scale_level: in
         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
 
     Returns:
-        Constraint: network_purchase_constraint
+        Constraint: network_purchase
     """
     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
 
     def network_purchase_rule(instance, resource, *scale_list):
         return instance.B_network[resource, scale_list] == sum(instance.B_location[location_, resource, scale_list] for location_ in instance.locations)
-    instance.network_purchase_constraint = Constraint(
+    instance.constraint_network_purchase = Constraint(
         instance.resources_purch, *scales, rule=network_purchase_rule, doc='total purchase from network')
     constraint_latex_render(network_purchase_rule)
-    return instance.network_purchase_constraint
+    return instance.constraint_network_purchase
diff --git a/src/energiapy/model/constraints/transport.py b/src/energiapy/model/constraints/transport.py
index 1e622d44..8af1dbbe 100644
--- a/src/energiapy/model/constraints/transport.py
+++ b/src/energiapy/model/constraints/transport.py
@@ -24,7 +24,17 @@
 
 
 # *-------------------------Transport constraints--------------------------
-def transport_export_constraint(instance: ConcreteModel, scheduling_scale_level: int = 0, transport_avail_dict: dict = {}) -> Constraint:
+def constraint_transport_export(instance: ConcreteModel, scheduling_scale_level: int = 0, transport_avail_dict: dict = {}) -> Constraint:
+    """Total resource exported equals amount transported through all modes
+
+    Args:
+        instance (ConcreteModel): pyomo model instance
+        scheduling_scale_level (int, optional): scale of scheduling decisions. Defaults to 0.
+        transport_avail_dict (dict, optional): Modes of transportation available between locations. Defaults to {}.
+
+    Returns:
+        Constraint: transport_export
+    """
     scales = scale_list(instance=instance,
                         scale_levels=scheduling_scale_level+1)
 
@@ -32,13 +42,23 @@ def transport_export_rule(instance, source, sink, resource, *scale_list):
         return instance.Exp[source, sink, resource, scale_list[:scheduling_scale_level+1]] == \
             sum(instance.Trans_exp[source, sink, resource, transport_, scale_list[:scheduling_scale_level+1]]
                 for transport_ in instance.transports.intersection(transport_avail_dict[(source, sink)]))
-    instance.transport_export_constraint = Constraint(instance.sources, instance.sinks,
+    instance.constraint_transport_export = Constraint(instance.sources, instance.sinks,
                                                       instance.resources_trans, *scales, rule=transport_export_rule, doc='export of resource from source to sink')
     constraint_latex_render(transport_export_rule)
-    return instance.transport_export_constraint
+    return instance.constraint_transport_export
+
 
+def constraint_transport_import(instance: ConcreteModel, scheduling_scale_level: int = 0, transport_avail_dict: dict = {}) -> Constraint:
+    """Total amount of resource imported
 
-def transport_import_constraint(instance: ConcreteModel, scheduling_scale_level: int = 0, transport_avail_dict: dict = {}) -> Constraint:
+    Args:
+        instance (ConcreteModel): pyomo model instance
+        scheduling_scale_level (int, optional): scale of scheduling decisions. Defaults to 0.
+        transport_avail_dict (dict, optional): Modes of transportation available between locations. Defaults to {}.
+
+    Returns:
+        Constraint: transport_import
+    """
     scales = scale_list(instance=instance,
                         scale_levels=scheduling_scale_level+1)
 
@@ -46,25 +66,45 @@ def transport_import_rule(instance, sink, source, resource, *scale_list):
         return instance.Imp[sink, source, resource, scale_list[:scheduling_scale_level+1]] == \
             sum(instance.Trans_imp[sink, source, resource, transport_, scale_list[:scheduling_scale_level+1]]
                 for transport_ in instance.transports.intersection(transport_avail_dict[(source, sink)]))
-    instance.transport_import_constraint = Constraint(instance.sinks, instance.sources,
+    instance.constraint_transport_import = Constraint(instance.sinks, instance.sources,
                                                       instance.resources_trans, *scales, rule=transport_import_rule, doc='import of resource from sink to source')
     constraint_latex_render(transport_import_rule)
-    return instance.transport_import_constraint
+    return instance.constraint_transport_import
+
 
+def constraint_transport_balance(instance: ConcreteModel, scheduling_scale_level: int = 0) -> Constraint:
+    """Imported resources from sink to source equals exported resources from sink to source
 
-def transport_balance_constraint(instance: ConcreteModel, scheduling_scale_level: int = 0) -> Constraint:
+    Args:
+        instance (ConcreteModel): pyomo model instance
+        scheduling_scale_level (int, optional): scale of scheduling decisions. Defaults to 0.
+
+    Returns:
+        Constraint: transport_balance
+    """
     scales = scale_list(instance=instance,
                         scale_levels=scheduling_scale_level+1)
 
     def transport_balance_rule(instance, sink, source, resource, *scale_list):
         return instance.Imp[sink, source, resource, scale_list[:scheduling_scale_level+1]] == instance.Exp[source, sink, resource, scale_list[:scheduling_scale_level+1]]
-    instance.transport_balance_constraint = Constraint(instance.sinks, instance.sources,
+    instance.constraint_transport_balance = Constraint(instance.sinks, instance.sources,
                                                        instance.resources_trans, *scales, rule=transport_balance_rule, doc='balances import and export from source to sinks')
     constraint_latex_render(transport_balance_rule)
-    return instance.transport_balance_constraint
+    return instance.constraint_transport_balance
+
+
+def constraint_transport_exp_UB(instance: ConcreteModel, scheduling_scale_level: int = 0, trans_max: dict = {}, transport_avail_dict: dict = {}) -> Constraint:
+    """Maximum resource that can be transported
 
+    Args:
+        instance (ConcreteModel): pyomo model instance
+        scheduling_scale_level (int, optional): scale of scheduling decisions. Defaults to 0.
+        trans_max (dict, optional): Maximum allowed transportation. Defaults to {}.
+        transport_avail_dict (dict, optional): Modes of transportation available between locations. Defaults to {}.
 
-def transport_exp_UB_constraint(instance: ConcreteModel, scheduling_scale_level: int = 0, trans_max: dict = {}, transport_avail_dict: dict = {}) -> Constraint:
+    Returns:
+        Constraint: transport_exp_UB
+    """
     scales = scale_list(instance=instance,
                         scale_levels=scheduling_scale_level+1)
 
@@ -73,13 +113,24 @@ def transport_exp_UB_rule(instance, source, sink, resource, transport, *scale_li
             return instance.Trans_exp[source, sink, resource, transport, scale_list[:scheduling_scale_level+1]] <= trans_max[transport]
         else:
             return instance.Trans_exp[source, sink, resource, transport, scale_list[:scheduling_scale_level+1]] <= 0
-    instance.transport_exp_UB_constraint = Constraint(instance.sources, instance.sinks, instance.resources_trans,
+    instance.constraint_transport_exp_UB = Constraint(instance.sources, instance.sinks, instance.resources_trans,
                                                       instance.transports, *scales, rule=transport_exp_UB_rule, doc='import of resource from sink to source')
     constraint_latex_render(transport_exp_UB_rule)
-    return instance.transport_exp_UB_constraint
+    return instance.constraint_transport_exp_UB
+
+
+def constraint_transport_imp_UB(instance: ConcreteModel, scheduling_scale_level: int = 0, trans_max: dict = {}, transport_avail_dict: dict = {}) -> Constraint:
+    """Maximum amount of resource that can be imported
 
+    Args:
+        instance (ConcreteModel): pyomo model instance
+        scheduling_scale_level (int, optional): scale of scheduling decisions. Defaults to 0.
+        trans_max (dict, optional): Maximum allowed transportation. Defaults to {}.
+        transport_avail_dict (dict, optional): Modes of transportation available between locations. Defaults to {}.
 
-def transport_imp_UB_constraint(instance: ConcreteModel, scheduling_scale_level: int = 0, trans_max: dict = {}, transport_avail_dict: dict = {}) -> Constraint:
+    Returns:
+        Constraint: transport_imp_UB
+    """
     scales = scale_list(instance=instance,
                         scale_levels=scheduling_scale_level+1)
 
@@ -88,7 +139,7 @@ def transport_imp_UB_rule(instance, sink, source, resource, transport, *scale_li
             return instance.Trans_imp[sink, source, resource, transport, scale_list[:scheduling_scale_level+1]] <= trans_max[transport]
         else:
             return instance.Trans_imp[sink, source, resource, transport, scale_list[:scheduling_scale_level+1]] <= 0
-    instance.transport_imp_UB_constraint = Constraint(instance.sinks, instance.sources, instance.resources_trans,
+    instance.constraint_transport_imp_UB = Constraint(instance.sinks, instance.sources, instance.resources_trans,
                                                       instance.transports, *scales, rule=transport_imp_UB_rule, doc='import of resource from sink to source')
     constraint_latex_render(transport_imp_UB_rule)
-    return instance.transport_imp_UB_constraint
+    return instance.constraint_transport_imp_UB
diff --git a/src/energiapy/model/constraints/uncertain.py b/src/energiapy/model/constraints/uncertain.py
index c6a8ca6c..bf64b3da 100644
--- a/src/energiapy/model/constraints/uncertain.py
+++ b/src/energiapy/model/constraints/uncertain.py
@@ -20,132 +20,7 @@
 from typing import Union
 from enum import Enum, auto
 
-# def demand_constraint_flex(instance: ConcreteModel, demand: int, demand_factor: Union[dict, float], \
-#     demand_scale_level: int = 0, scheduling_scale_level: int = 0) -> Constraint:
-#     """Ensures that demand for resource is met at chosen temporal scale
-
-#     Args:
-#         instance (ConcreteModel): pyomo instance
-#         demand_scale_level (int, optional): scale of demand decisions. Defaults to 0.
-#         scheduling_scale_level (int, optional): scale of scheduling decisions. Defaults to 0.
-#         demand_dict (dict, optional): demand at location. Defaults to {}.
-
-#     Returns:
-#         Constraint: demand_constraint
-#     """
-#     # scales = scale_list(instance= instance, scale_levels = demand_scale_level+1)
-#     scales = scale_list(instance=instance,
-#                         scale_levels=instance.scales.__len__())
-#     scale_iter = scale_tuple(
-#         instance=instance, scale_levels=scheduling_scale_level+1)
- 
-#     def demand_flex_rule(instance, location, resource, *scale_list):
-#         if type(demand_factor[location][list(demand_factor[location])[0]]) == float:
-#             return sum(instance.S[location, resource_, scale_list[:scheduling_scale_level+1]] for
-#                        resource_ in instance.resources_demand) == demand*demand_factor[location][scale_list[:demand_scale_level+1]] - instance.Demand_slack[location, scale_list[:demand_scale_level+1]]
-#         else:
-#             return sum(instance.S[location, resource, scale_] for scale_ in scale_iter if scale_[:scheduling_scale_level+1] == scale_list)\
-#                 == demand*demand_factor[location][resource][scale_list[:demand_scale_level+1]] - instance.Demand_slack[location, scale_list[:demand_scale_level+1]]
-
-#     if len(instance.locations) > 1:
-#         instance.demand_constraint_flex = Constraint(
-#             instance.sinks, instance.resources_demand, *scales, rule=demand_flex_rule, doc='specific demand for resources')
-#     else:
-#         instance.demand_constraint_flex = Constraint(
-#             instance.locations, instance.resources_demand, *scales, rule=demand_flex_rule, doc='specific demand for resources')
-#     constraint_latex_render(demand_flex_rule)
-#     return instance.demand_constraint_flex
-
-
-
-# def delta_cap_location_constraint(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
-#     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
-#     scale_iter = scale_tuple(
-#         instance=instance, scale_levels=instance.scales.__len__())
-
-#     def delta_cap_location_rule(instance, location, process, *scale_list):
-#         return instance.Delta_Cap_P_location[location, process, scale_list] == sum(instance.Delta_Cap_P[location, process, scale_] for scale_ in scale_iter)
-#     instance.delta_cap_location_constraint = Constraint(
-#         instance.locations, instance.processes_varying, *scales, rule=delta_cap_location_rule, doc='total transport cost across scale')
-#     constraint_latex_render(delta_cap_location_rule)
-#     return instance.delta_cap_location_constraint
-
-
-# def delta_cap_network_constraint(instance: ConcreteModel, network_scale_level: int = 0) -> Constraint:
-#     scales = scale_list(instance=instance, scale_levels=network_scale_level+1)
-
-#     def delta_cap_network_rule(instance, process, *scale_list):
-#         return instance.Delta_Cap_P_network[process, scale_list] == sum(instance.Delta_Cap_P_location[location_, process, scale_list] for location_ in instance.locations)
-#     instance.delta_cap_network_constraint = Constraint(
-#         instance.processes_varying, *scales, rule=delta_cap_network_rule, doc='total transport cost across scale')
-#     constraint_latex_render(delta_cap_network_rule)
-#     return instance.delta_cap_network_constraint
-
-
-
-# def uncertain_nameplate_production_constraint(instance: ConcreteModel, network_scale_level: int = 0, scheduling_scale_level: int = 0) -> Constraint:
-#     """Determines production capacity utilization of facilities at location in network and capacity of facilities 
-#     with uncertain capacility available for utilization
-
-#     Args:
-#         instance (ConcreteModel): pyomo instance
-#         network_scale_level (int, optional): scale of network decisions. Defaults to 0.
-#         scheduling_scale_level (int, optional): scale of scheduling decisions. Defaults to 0.
-
-#     Returns:
-#         Constraint: uncertain_nameplate_production_constraint
-#     """
-#     scales = scale_list(instance=instance,
-#                         scale_levels=instance.scales.__len__())
-
-#     def uncertain_nameplate_production_rule(instance, location, process, *scale_list):
-#         if process in instance.processes_varying:
-#             return instance.P[location, process, scale_list[:scheduling_scale_level+1]] <= instance.Cap_P[location, process, scale_list[:network_scale_level+1]]\
-#                 + instance.Delta_Cap_P[location, process,
-#                                        scale_list[:scheduling_scale_level+1]]
-#         else:
-#             return instance.P[location, process, scale_list[:scheduling_scale_level+1]] <= instance.Cap_P[location, process, scale_list[:network_scale_level+1]]
-#     instance.uncertain_nameplate_production_constraint = Constraint(
-#         instance.locations, instance.processes, *scales, rule=uncertain_nameplate_production_rule, doc='nameplate production capacity constraint')
-#     constraint_latex_render(uncertain_nameplate_production_rule)
-#     return instance.uncertain_nameplate_production_constraint
-
-
-# def uncertain_resource_purchase_constraint(instance: ConcreteModel, price: dict = {},  loc_res_dict: dict = {}, scheduling_scale_level: int = 0, expenditure_scale_level: int = 0) -> Constraint:
-#     """Determines expenditure on resource at location in network at the scheduling/expenditure scale
-#     with uncertainty in resource proce
-
-#     Args:
-#         instance (ConcreteModel): pyomo instance
-#         price (dict, optional): base price of resource. Defaults to {}.
-#         loc_res_dict (dict, optional): storage facilities for resource available at location. Defaults to {}.
-#         scheduling_scale_level (int, optional): scale of scheduling decisions. Defaults to 0.
-#         expenditure_scale_level (int, optional): scale of expenditure decisions. Defaults to 0.
-
-#     Returns:
-#         Constraint: uncertain_resource_purchase_constraint
-#     """
-#     scales = scale_list(instance=instance,
-#                         scale_levels=instance.scales.__len__())
-
-#     def uncertain_resource_purchase_rule(instance, location, resource, *scale_list):
-#         if resource in instance.resources_varying.intersection(loc_res_dict[location]):
-#             return instance.B[location, resource, scale_list[:scheduling_scale_level+1]] == price[location][resource] *\
-#                 instance.C[location, resource, scale_list[:scheduling_scale_level+1]] * \
-#                 instance.Delta_Cost_R[location, resource,
-#                                       scale_list[:scheduling_scale_level+1]]
-#         else:
-#             if resource in instance.resources_purch.intersection(loc_res_dict[location]):
-#                 return instance.B[location, resource, scale_list[:scheduling_scale_level+1]] == price[location][resource]*instance.C[location, resource, scale_list[:scheduling_scale_level+1]]
-#             else:
-#                 return instance.B[location, resource, scale_list[:scheduling_scale_level+1]] == 0
-#     instance.uncertain_resource_purchase_constraint = Constraint(
-#         instance.locations, instance.resources_purch, *scales, rule=uncertain_resource_purchase_rule, doc='expenditure on purchase of resource')
-#     constraint_latex_render(uncertain_resource_purchase_rule)
-#     return instance.uncertain_resource_purchase_constraint
-
-
-def uncertain_resource_demand_constraint(instance: ConcreteModel, demand:Union[dict, float], scheduling_scale_level: int = 0) -> Constraint:
+def constraint_uncertain_resource_demand(instance: ConcreteModel, demand:Union[dict, float], scheduling_scale_level: int = 0) -> Constraint:
     """Discharge meets an uncertain demand
 
     Args:
@@ -154,7 +29,7 @@ def uncertain_resource_demand_constraint(instance: ConcreteModel, demand:Union[d
         scheduling_scale_level (int, optional): scheduling scales of the problem. Defaults to 0.
 
     Returns:
-        Constraint: instance.uncertain_resource_demand_constraint
+        Constraint: instance.uncertain_resource_demand
     """
     
     scales = scale_tuple(instance=instance, scale_levels=scheduling_scale_level+1)
@@ -165,11 +40,11 @@ def uncertain_resource_demand_rule(instance, location, resource, *scale_list):
         else:
             return instance.S[location, resource, scale_list[:scheduling_scale_level+1]] >= demand*instance.resource_demand_uncertainty[location, resource, scale_list[:scheduling_scale_level+1]]
             
-    instance.uncertain_resource_demand_constraint = Constraint(instance.locations, instance.resources_uncertain_demand, *scales, rule= uncertain_resource_demand_rule,\
+    instance.constraint_uncertain_resource_demand = Constraint(instance.locations, instance.resources_uncertain_demand, *scales, rule= uncertain_resource_demand_rule,\
         doc = 'meet uncertain demand')
-    return instance.uncertain_resource_demand_constraint
+    return instance.constraint_uncertain_resource_demand
 
-def uncertain_process_capacity_constraint(instance: ConcreteModel, capacity: dict, network_scale_level: int = 0) -> Constraint:
+def constraint_uncertain_process_capacity(instance: ConcreteModel, capacity: dict, network_scale_level: int = 0) -> Constraint:
     """provides uncertainty to process capacity
 
     Args:
@@ -178,13 +53,13 @@ def uncertain_process_capacity_constraint(instance: ConcreteModel, capacity: dic
         network_scale_level (int, optional): network scales of the problem. Defaults to 0.
 
     Returns:
-        Constraint: _description_
+        Constraint: uncertain_process_capacity
     """
     scales = scale_tuple(instance=instance, scale_levels=network_scale_level+1)
     
     def uncertain_process_capacity_rule(instance, location, process, *scale_list):
         return instance.Cap_P[location, process, scale_list[:network_scale_level+1]] == capacity[location][process]*instance.process_capacity_uncertainty[location, process, scale_list[:network_scale_level+1]]
     
-    instance.uncertain_process_capacity_constraint = Constraint(instance.locations, instance.processes_uncertain_capacity, *scales, rule= uncertain_process_capacity_rule,\
+    instance.constraint_uncertain_process_capacity = Constraint(instance.locations, instance.processes_uncertain_capacity, *scales, rule= uncertain_process_capacity_rule,\
         doc = 'uncertain capacity for process')
-    return instance.uncertain_process_capacity_constraint
+    return instance.constraint_uncertain_process_capacity
diff --git a/src/energiapy/model/formulate.py b/src/energiapy/model/formulate.py
index ca868067..05e297f9 100644
--- a/src/energiapy/model/formulate.py
+++ b/src/energiapy/model/formulate.py
@@ -28,20 +28,18 @@
 from .constraints.transport import *
 from .constraints.failure import *
 from .constraints.uncertain import *
+from .constraints.mode import *
 from .objectives import *
 from pyomo.environ import ConcreteModel
 from typing import  Set
 
 
-# class Variables(Enum):
-#     network = auto()
-#     schedule = auto()
-#     binary = auto()
-#     cost = auto()
-#     transport = auto()
-#     uncertain = auto()
-    
 class Constraints(Enum):
+    """Class of constraints
+
+    Args:
+        Enum (auto): choose set of contraints to add
+    """
     cost = auto()
     emission = auto()
     failure = auto()
@@ -55,6 +53,11 @@ class Constraints(Enum):
     lifecycle = auto()
 
 class Objective(Enum):
+    """Class of objective
+
+    Args:
+        Enum (auto): cost, demand, etc.
+    """
     cost = auto()
     demand = auto()
     
@@ -81,7 +84,7 @@ def formulate(scenario: Scenario, constraints:Set[Constraints], objective:Object
     Objectives include:
             Objectives.cost
             Objectives.demand
-            
+      
     Returns:
         ConcreteModel: instance
     """
@@ -101,164 +104,165 @@ def formulate(scenario: Scenario, constraints:Set[Constraints], objective:Object
     if Constraints.uncertain in constraints:
         generate_uncertainty_vars(instance = instance, scale_level = scenario.scheduling_scale_level)
 
-        uncertain_process_capacity_constraint(instance = instance, capacity = scenario.prod_max, network_scale_level = scenario.network_scale_level)
-        uncertain_resource_demand_constraint(instance = instance, demand = demand, scheduling_scale_level = scenario.scheduling_scale_level)
+        constraint_uncertain_process_capacity(instance = instance, capacity = scenario.prod_max, network_scale_level = scenario.network_scale_level)
+        constraint_uncertain_resource_demand(instance = instance, demand = demand, scheduling_scale_level = scenario.scheduling_scale_level)
     
     if Constraints.mode in constraints:
-        generate_mode_vars(instance=instance, scale_level=scenario.scheduling_scale_level, mode_dict= scenario.mode_dict)
-
+        generate_mode_vars(instance= instance, scale_level=scenario.scheduling_scale_level, mode_dict= scenario.mode_dict)
+        
+        constraint_production_mode(instance= instance, mode_dict= scenario.mode_dict, scheduling_scale_level= scenario.scheduling_scale_level)
+        constraint_production_mode_facility(instance= instance, prod_max= scenario.prod_max, loc_pro_dict= scenario.loc_pro_dict, scheduling_scale_level= scenario.scheduling_scale_level)
+        constraint_production_mode_binary(instance= instance, mode_dict= scenario.mode_dict, scheduling_scale_level= scenario.scheduling_scale_level, network_scale_level= scenario.network_scale_level)
     
     if len(scenario.location_set) > 1:
         generate_transport_vars(instance=instance, scale_level=scenario.scheduling_scale_level)
         
     if Constraints.cost in constraints:
-        process_capex_constraint(instance=instance, capex_dict=scenario.capex_dict,
+        constraint_process_capex(instance=instance, capex_dict=scenario.capex_dict,
                              network_scale_level=scenario.network_scale_level)
-        process_fopex_constraint(instance=instance, fopex_dict=scenario.fopex_dict,
+        constraint_process_fopex(instance=instance, fopex_dict=scenario.fopex_dict,
                                 network_scale_level=scenario.network_scale_level)
-        process_vopex_constraint(instance=instance, vopex_dict=scenario.vopex_dict,
+        constraint_process_vopex(instance=instance, vopex_dict=scenario.vopex_dict,
                                 network_scale_level=scenario.network_scale_level)
 
-        process_incidental_constraint(instance=instance, incidental_dict=scenario.incidental_dict,
+        constraint_process_incidental(instance=instance, incidental_dict=scenario.incidental_dict,
                                 network_scale_level=scenario.network_scale_level)
 
         
-        location_capex_constraint(
+        constraint_location_capex(
             instance=instance, network_scale_level=scenario.network_scale_level)
-        location_fopex_constraint(
+        constraint_location_fopex(
             instance=instance, network_scale_level=scenario.network_scale_level)
-        location_vopex_constraint(
+        constraint_location_vopex(
             instance=instance, network_scale_level=scenario.network_scale_level)
-        location_incidental_constraint(
+        constraint_location_incidental(
             instance=instance, network_scale_level=scenario.network_scale_level)
         
-        network_capex_constraint(
+        constraint_network_capex(
             instance=instance, network_scale_level=scenario.network_scale_level)
-        network_fopex_constraint(
+        constraint_network_fopex(
             instance=instance, network_scale_level=scenario.network_scale_level)
-        network_vopex_constraint(
+        constraint_network_vopex(
             instance=instance, network_scale_level=scenario.network_scale_level)
         
-        network_incidental_constraint(
+        constraint_network_incidental(
             instance=instance, network_scale_level=scenario.network_scale_level)
         
     if Constraints.emission in constraints:
-        global_warming_potential_process_constraint(
+        constraint_global_warming_potential_process(
             instance = instance, process_gwp_dict = scenario.process_gwp_dict, network_scale_level=scenario.network_scale_level)
         
-        global_warming_potential_resource_constraint(
+        constraint_global_warming_potential_resource(
             instance = instance, resource_gwp_dict = scenario.resource_gwp_dict, network_scale_level=scenario.network_scale_level)
 
-        global_warming_potential_material_constraint(
+        constraint_global_warming_potential_material(
             instance = instance, material_gwp_dict = scenario.material_gwp_dict, process_material_dict = scenario.process_material_dict, network_scale_level=scenario.network_scale_level)
 
-        global_warming_potential_location_constraint(
+        constraint_global_warming_potential_location(
             instance=instance, network_scale_level=scenario.network_scale_level)
 
-        global_warming_potential_network_constraint(
+        constraint_global_warming_potential_network(
             instance=instance, network_scale_level=scenario.network_scale_level)
 
     if Constraints.failure in constraints:
-        nameplate_production_failure_constraint(instance=instance, fail_factor=scenario.fail_factor,
+        constraint_nameplate_production_failure(instance=instance, fail_factor=scenario.fail_factor,
                                 network_scale_level=scenario.network_scale_level, scheduling_scale_level=scenario.scheduling_scale_level)
 
     if Constraints.inventory in constraints:
-        nameplate_inventory_constraint(instance=instance, loc_res_dict=scenario.loc_res_dict, network_scale_level=scenario.network_scale_level,
+        constraint_nameplate_inventory(instance=instance, loc_res_dict=scenario.loc_res_dict, network_scale_level=scenario.network_scale_level,
                                 scheduling_scale_level=scenario.scheduling_scale_level)
 
-        storage_facility_constraint(instance=instance, store_max=scenario.store_max,
+        constraint_storage_facility(instance=instance, store_max=scenario.store_max,
                             loc_res_dict=scenario.loc_res_dict, network_scale_level=scenario.network_scale_level)
 
-        min_storage_facility_constraint(instance=instance, store_min=scenario.store_min,
+        constraint_min_storage_facility(instance=instance, store_min=scenario.store_min,
                                         loc_res_dict=scenario.loc_res_dict, network_scale_level=scenario.network_scale_level)
 
     if Constraints.production in constraints:
-        nameplate_production_constraint(instance=instance, capacity_factor=scenario.capacity_factor, loc_pro_dict=scenario.loc_pro_dict,
+        constraint_nameplate_production(instance=instance, capacity_factor=scenario.capacity_factor, loc_pro_dict=scenario.loc_pro_dict,
                                 network_scale_level=scenario.network_scale_level, scheduling_scale_level=scenario.scheduling_scale_level)
-        production_facility_constraint(instance=instance, prod_max=scenario.prod_max,
+        constraint_production_facility(instance=instance, prod_max=scenario.prod_max,
                                 loc_pro_dict=scenario.loc_pro_dict, network_scale_level=scenario.network_scale_level)
-        min_production_facility_constraint(instance=instance, prod_min=scenario.prod_min,
+        constraint_min_production_facility(instance=instance, prod_min=scenario.prod_min,
                                     loc_pro_dict=scenario.loc_pro_dict, network_scale_level=scenario.network_scale_level)
 
     if Constraints.land in constraints:
-        process_land_constraint(instance=instance, land_dict=scenario.land_dict,
+        constraint_process_land(instance=instance, land_dict=scenario.land_dict,
                         network_scale_level=scenario.network_scale_level)
-        location_land_constraint(
+        constraint_location_land(
             instance=instance, network_scale_level=scenario.network_scale_level)
-        network_land_constraint(
+        constraint_network_land(
             instance=instance, network_scale_level=scenario.network_scale_level)
         
-        process_land_cost_constraint(instance=instance, land_dict=scenario.land_dict, land_cost_dict = scenario.land_cost_dict,\
+        constraint_process_land_cost(instance=instance, land_dict=scenario.land_dict, land_cost_dict = scenario.land_cost_dict,\
             network_scale_level=scenario.network_scale_level)
-        location_land_cost_constraint(
+        constraint_location_land_cost(
             instance=instance, network_scale_level=scenario.network_scale_level)
-        network_land_cost_constraint(
+        constraint_network_land_cost(
             instance=instance, network_scale_level=scenario.network_scale_level)
         
         if land_restriction is not None:
-            location_land_restriction_constraint(instance=instance, network_scale_level=scenario.network_scale_level, land_restriction= land_restriction)
+            constraint_location_land_restriction(instance=instance, network_scale_level=scenario.network_scale_level, land_restriction= land_restriction)
 
     if Constraints.resource_balance in constraints:
         
-        inventory_balance_constraint(instance=instance, scheduling_scale_level=scenario.scheduling_scale_level,
-                                    conversion=scenario.conversion)
+        constraint_inventory_balance(instance=instance, scheduling_scale_level=scenario.scheduling_scale_level,
+                                    multiconversion=scenario.multiconversion, mode_dict= scenario.mode_dict)
 
-        resource_consumption_constraint(instance=instance, loc_res_dict=scenario.loc_res_dict,
+        constraint_resource_consumption(instance=instance, loc_res_dict=scenario.loc_res_dict,
                                 cons_max=scenario.cons_max, scheduling_scale_level=scenario.scheduling_scale_level)
         
-        resource_purchase_constraint(instance=instance, cost_factor=scenario.cost_factor, price=scenario.price,
+        constraint_resource_purchase(instance=instance, cost_factor=scenario.cost_factor, price=scenario.price,
                                     loc_res_dict=scenario.loc_res_dict, scheduling_scale_level=scenario.scheduling_scale_level,
                                     expenditure_scale_level=scenario.expenditure_scale_level)
 
-        location_production_constraint(
+        constraint_location_production(
             instance=instance, network_scale_level=scenario.network_scale_level, cluster_wt=scenario.cluster_wt)
-        location_discharge_constraint(
+        constraint_location_discharge(
             instance=instance, network_scale_level=scenario.network_scale_level, cluster_wt=scenario.cluster_wt)
-        location_consumption_constraint(
+        constraint_location_consumption(
             instance=instance, network_scale_level=scenario.network_scale_level, cluster_wt=scenario.cluster_wt)
-        location_purchase_constraint(
+        constraint_location_purchase(
             instance=instance, network_scale_level=scenario.network_scale_level, cluster_wt=scenario.cluster_wt)
 
-        network_production_constraint(
+        constraint_network_production(
             instance=instance, network_scale_level=scenario.network_scale_level)
-        network_discharge_constraint(
+        constraint_network_discharge(
             instance=instance, network_scale_level=scenario.network_scale_level)
-        network_consumption_constraint(
+        constraint_network_consumption(
             instance=instance, network_scale_level=scenario.network_scale_level)
-        network_purchase_constraint(
+        constraint_network_purchase(
             instance=instance, network_scale_level=scenario.network_scale_level)
 
     if Constraints.transport in constraints:
         
         if len(scenario.location_set) > 1:
-            transport_export_constraint(instance=instance, scheduling_scale_level=scenario.scheduling_scale_level,
+            constraint_transport_export(instance=instance, scheduling_scale_level=scenario.scheduling_scale_level,
                                         transport_avail_dict=scenario.transport_avail_dict)
-            transport_import_constraint(instance=instance, scheduling_scale_level=scenario.scheduling_scale_level,
+            constraint_transport_import(instance=instance, scheduling_scale_level=scenario.scheduling_scale_level,
                                         transport_avail_dict=scenario.transport_avail_dict)
-            transport_exp_UB_constraint(instance=instance, scheduling_scale_level=scenario.scheduling_scale_level,
+            constraint_transport_exp_UB(instance=instance, scheduling_scale_level=scenario.scheduling_scale_level,
                                         trans_max=scenario.trans_max, transport_avail_dict=scenario.transport_avail_dict)
-            transport_imp_UB_constraint(instance=instance, scheduling_scale_level=scenario.scheduling_scale_level,
+            constraint_transport_imp_UB(instance=instance, scheduling_scale_level=scenario.scheduling_scale_level,
                                         trans_max=scenario.trans_max, transport_avail_dict=scenario.transport_avail_dict)
-            transport_balance_constraint(
+            constraint_transport_balance(
                 instance=instance, scheduling_scale_level=scenario.scheduling_scale_level)
 
-            transport_exp_cost_constraint(instance=instance, scheduling_scale_level=scenario.scheduling_scale_level,
-                                        trans_cost=scenario.trans_cost, distance_dict=scenario.distance_dict)
-            transport_imp_cost_constraint(instance=instance, scheduling_scale_level=scenario.scheduling_scale_level,
+            constraint_transport_imp_cost(instance=instance, scheduling_scale_level=scenario.scheduling_scale_level,
                                         trans_cost=scenario.trans_cost, distance_dict=scenario.distance_dict)
-            transport_cost_constraint(
+            constraint_transport_cost(
                 instance=instance, scheduling_scale_level=scenario.scheduling_scale_level)
-            transport_cost_network_constraint(
+            constraint_transport_cost_network(
                 instance=instance, network_scale_level=scenario.network_scale_level)
     
     if gwp is not None:
-        global_warming_potential_network_reduction_constraint(instance= instance, network_scale_level = scenario.network_scale_level\
+        constraint_global_warming_potential_network_reduction(instance= instance, network_scale_level = scenario.network_scale_level\
             , gwp_reduction_pct = gwp_reduction_pct, gwp = gwp)
 
             
     if objective == Objective.cost:
         
-        demand_constraint(instance=instance, demand_scale_level=scenario.demand_scale_level,
+        constraint_demand(instance=instance, demand_scale_level=scenario.demand_scale_level,
                     scheduling_scale_level=scenario.scheduling_scale_level, demand = demand, demand_factor=scenario.demand_factor)
         
         cost_objective(instance=instance,
diff --git a/src/energiapy/model/sets.py b/src/energiapy/model/sets.py
index 34b1e2f3..e07eb67b 100644
--- a/src/energiapy/model/sets.py
+++ b/src/energiapy/model/sets.py
@@ -15,7 +15,7 @@
 from ..components.temporal_scale import Temporal_scale
 from pyomo.environ import ConcreteModel, Set
 from enum import Enum, auto
-
+from itertools import product
 
 def generate_sets(instance: ConcreteModel, scenario:Scenario):
     """Generates pyomo sets based on declared lists.
@@ -76,9 +76,9 @@ def generate_sets(instance: ConcreteModel, scenario:Scenario):
     """
 
     instance.scales = Set(scenario.scales.list, initialize = scenario.scales.scale, doc = 'set of scales')
-
-    sets = scenario.set_dict
     
+    sets = scenario.set_dict
+       
     instance.processes = Set(initialize  = sets['processes'], doc = 'Set of processes')
     instance.processes_full = Set(initialize = sets['processes_full'], doc = 'Set of all processes including dummy discharge')
     instance.resources = Set(initialize  = sets['resources'], doc = 'Set of resources')
@@ -101,6 +101,12 @@ def generate_sets(instance: ConcreteModel, scenario:Scenario):
     instance.resources_uncertain_demand = Set(initialize = sets['resources_uncertain_demand'], doc = 'Set of resources with uncertain demand')
     instance.processes_uncertain_capacity= Set(initialize = sets['processes_uncertain_capacity'], doc = 'Set of processes with uncertain capacity')
     
+    mode_lens  = []
+    for i,j in product(scenario.process_set, scenario.location_set):
+        mode_lens.append(len(scenario.prod_max[j.name][i.name].keys()))
+        
+    instance.modes = Set(initialize = list(range(max(mode_lens))), doc = 'Set of process modes')
+    
     
     if scenario.source_locations is not None:
         instance.sources = Set(initialize = sets['sources'], doc = 'Set of sources')
diff --git a/src/energiapy/model/variables/binary.py b/src/energiapy/model/variables/binary.py
index 99cf64c4..24228a4e 100644
--- a/src/energiapy/model/variables/binary.py
+++ b/src/energiapy/model/variables/binary.py
@@ -18,12 +18,6 @@ def generate_network_binary_vars(instance: ConcreteModel, scale_level:int= 0):
     """declares the following binary variables:
 
     scales_network_binary: Set of scales to define the network level
-
-    .. math::
-        X^{P}_{location, process, scale_{network}} \\textnormal{Process binary, 1 if process is set up, 0 otherwise}
-    
-    .. math::
-        X^{S}_{location, resource, scale_{network}}: \\textnormal{Storage binary variable, 1 if storage for resource is set up, 0 otherwise}
     
     Args:
         instance (ConcreteModel): pyomo instance
diff --git a/src/energiapy/model/variables/mode.py b/src/energiapy/model/variables/mode.py
index e05a4850..3eb9f4a2 100644
--- a/src/energiapy/model/variables/mode.py
+++ b/src/energiapy/model/variables/mode.py
@@ -31,4 +31,5 @@ def generate_mode_vars(instance: ConcreteModel, scale_level:int = 0, mode_dict:d
     """
     instance.P_m = Var(instance.locations, [(i,j) for i in mode_dict.keys() for j in mode_dict[i]], instance.scales_scheduling, within = NonNegativeReals, doc = 'Production modes')
     instance.X_P_m = Var(instance.locations, [(i,j) for i in mode_dict.keys() for j in mode_dict[i]], instance.scales_scheduling, within = Binary, doc = 'Production mode binaries')
+    instance.Cap_P_m = Var(instance.locations, [(i,j) for i in mode_dict.keys() for j in mode_dict[i]], instance.scales_scheduling, within = Binary, doc = 'Production mode binaries')
     return 
\ No newline at end of file
diff --git a/src/energiapy/plot/__init__.py b/src/energiapy/plot/__init__.py
index 2ab457df..e69de29b 100644
--- a/src/energiapy/plot/__init__.py
+++ b/src/energiapy/plot/__init__.py
@@ -1,11 +0,0 @@
-"""energiapy.plot init file
-"""
-
-__author__ = "Rahul Kakodkar"
-__copyright__ = "Copyright 2022, Multi-parametric Optimization & Control Lab"
-__credits__ = ["Rahul Kakodkar", "Efstratios N. Pistikopoulos"]
-__license__ = "Open"
-__version__ = "0.0.1"
-__maintainer__ = "Rahul Kakodkar"
-__email__ = "cacodcar@tamu.edu"
-__status__ = "Production"
diff --git a/src/energiapy/plot/plot.py b/src/energiapy/plot/plot.py
index 32f88ebd..fa5650d5 100644
--- a/src/energiapy/plot/plot.py
+++ b/src/energiapy/plot/plot.py
@@ -1,17 +1,17 @@
-#%%
 """plotting module
 """
 
 __author__ = "Rahul Kakodkar"
-__copyright__ = "Copyright 2022, Multi-parametric Optimization & Control Lab"
+__copyright__ = "Copyright 2023, Multi-parametric Optimization & Control Lab"
 __credits__ = ["Rahul Kakodkar", "Efstratios N. Pistikopoulos"]
 __license__ = "Open"
-__version__ = "0.0.1"
+__version__ = "1.0.4"
 __maintainer__ = "Rahul Kakodkar"
 __email__ = "cacodcar@tamu.edu"
 __status__ = "Production"
 
 from matplotlib import rc
+from numpy import zeros, array
 import matplotlib.pyplot as plt
 from ..utils.plot_utils import axis_formatter
 from ..components.process import Process
@@ -19,9 +19,30 @@
 from ..components.result import Result
 from ..components.location import Location
 from typing import Union
+from enum import Enum, auto
 
 
+class CostX(Enum):
+    """X axis for cost plot
 
+    Args:
+        Enum (_type_): location-wise or process-wise
+    """
+    location_wise = auto()
+    process_wise = auto()
+    
+class CostY(Enum):
+    """Y axis for cost plot
+    
+    Args:
+        Enum (_type_): capex, fopex, vopex, or total
+    """
+    total = auto()
+    capex = auto()
+    vopex = auto()
+    fopex = auto()
+    
+    
 
 def capacity_factor(process: Process, location: Location, \
     fig_size:tuple = (12,6), font_size:int = 16, color:str ='blue', usetex:bool = False):
@@ -232,13 +253,125 @@ def capacity_utilization(results: Result, location:str, process:str = None,\
         plt.rcdefaults()
     return
 
+def transport(results: Result, source:str, sink:str, resource: str, transport: str, fig_size:tuple = (12,6), font_size:int = 16, color:str ='blue', usetex:bool = False):
+    """Plots the transportation schedule from source to sink of choice for resource through a transportation mode
 
+    Args:
+        results (Result): results
+        source (str): source location
+        sink (str): sink location
+        resource (str): Resource that is being transported 
+        transport (str): Transport being used
+        fig_size (tuple, optional): Defaults to (12,6).
+        font_size (int, optional): Defaults to 16.
+        color (str, optional): Defaults to 'blue'.
+        usetex (bool, optional): Defaults to False.
+    
+    Examples:
+
+        Plotting transport is fairly straight forward. This plots the export from source to sink. 
+    
+        >>> plot.transport(results= results, source= 'Goa', sink= 'Texas', resource= 'PhDStudents', transport= 'GradSchool')
+    
+    """
+    
+    rc('font', **{'family': 'serif', 'serif': ['Computer Modern'], 'size': font_size})
+    rc('text', usetex=usetex)
+    fig, ax = plt.subplots(figsize= fig_size)
+    y_ = [results.output['Trans_exp'][i] for i in results.output['Trans_exp'].keys() if list(i)[:4] == [source, sink, resource, transport]]
+    plt.plot(y_)
+    plt.ylabel(f"Amount in unit basis")
+    plt.xlabel(f"Scheduling scale")
+    plt.title(f"Schedule for {resource} transported on {transport} from {source} to {sink}")
+    plt.xticks(rotation = 90)
+    plt.grid(alpha=0.3)
+    plt.rcdefaults()
+    return 
 
-#TODO - plots are independent of scales, check
 #TODO - make bar plots / pie plots for contribution from different components 
 #TODO - make layered scheduling plot for comparison 
 #TODO - make scenario comparison plots, perhaps use kwargs, allow n number of comparisons 
 
 
+def cost(results:Result,x: CostX, y: CostY, location: str = None, fig_size:tuple = (12,6), bar_width: float = 0.5, font_size:int = 16, color:str ='blue', usetex:bool = False):
+    """Plots the cost of processes, such as capex, vopex, fopex, or total
+
+    Args:
+        results (Result): results 
+        x (CostX): one of CostX.location_wise, CostX.process_wise 
+        y (CostY): one of CostY.total, CostY.capex, CostY.fopex, CostY.vopex
+        location (str, optional): location to plot for, applicable for CostX.process_wise. Defaults to None.
+        fig_size (tuple, optional): Defaults to (12,6).
+        bar_width (float, optional): Defaults to 0.5.
+        font_size (int, optional): Defaults to 16.
+        color (str, optional): Defaults to 'blue'.
+        usetex (bool, optional): Defaults to False.
+    """
+    
+    if y is CostY.capex:
+        res_dict = results.output['Capex_process']
+    if y is CostY.vopex:
+        res_dict = results.output['Vopex_process']
+    if y is CostY.fopex:
+        res_dict = results.output['Fopex_process']
+
+    if y is CostY.total:
+        res_dict = {i: results.output['Capex_process'][i] + results.output['Vopex_process'][i] + \
+            results.output['Fopex_process'][i] for i in results.output['Capex_process'].keys()}
+    
+    if x == CostX.process_wise:
+
+        x_,y_,n_ = ([] for _ in range(3))
+        for i in res_dict.keys():
+            if i[0] == location:
+                if res_dict[i]> 0:
+                    y_.append(res_dict[i])
+                    x_.append(i[1])
+                else:
+                    n_.append(i[1])
+                    
+        rc('font', **{'family': 'serif', 'serif': ['Computer Modern'], 'size': font_size})
+        rc('text', usetex=usetex)
+        fig, ax = plt.subplots(figsize= fig_size)
+        ax.bar(x_,y_, width = bar_width, zorder = 3)
+        y_plot = ''.join(str(y).split('CostY.')[1])
+        plt.title(f'Process-wise {y_plot} cost at {location}')
+        plt.ylabel(f'Unit Currency')
+        plt.xlabel(f'Processes')
+        plt.yscale('log')
+        plt.grid(alpha = 0.3, zorder = 0)
+        plt.rcdefaults()
+        
+    if x == CostX.location_wise:
+        
+        locations = tuple(results.components['locations'].keys())
+
+        weight_counts = dict()
+
+        for i in list(results.components['processes'].keys()):
+            vals = []
+            for j in res_dict.keys():
+                if j[1] == i:
+                    vals.append(res_dict[j]) 
+            weight_counts[i] = array(vals)        
+            
+        rc('font', **{'family': 'serif', 'serif': ['Computer Modern'], 'size': font_size})
+        rc('text', usetex=usetex)
+        fig, ax = plt.subplots(figsize= fig_size)
+        bottom = zeros(len(locations))
+
+        for boolean, weight_count in weight_counts.items():
+            p = ax.bar(locations, weight_count,  width = bar_width, label=boolean, bottom=bottom, zorder = 3)
+            bottom += weight_count
+        y_plot = ''.join(str(y).split('CostY.')[1])
+        plt.title(f'Location-wise {y_plot}')
+        plt.ylabel(f'Unit Currency')
+        plt.xlabel(f'Locations')
+        plt.yscale('log')
+        plt.legend()
+        plt.grid(alpha = 0.3, zorder = 0)
+        plt.rcdefaults()
+        plt.plot()
+    return
 
 
diff --git a/src/energiapy/utils/__init__.py b/src/energiapy/utils/__init__.py
index 6f8dd24e..e69de29b 100644
--- a/src/energiapy/utils/__init__.py
+++ b/src/energiapy/utils/__init__.py
@@ -1,11 +0,0 @@
-"""energiapy.utils init file
-"""
-
-__author__ = "Rahul Kakodkar"
-__copyright__ = "Copyright 2022, Multi-parametric Optimization & Control Lab"
-__credits__ = ["Rahul Kakodkar", "Efstratios N. Pistikopoulos"]
-__license__ = "Open"
-__version__ = "0.0.1"
-__maintainer__ = "Rahul Kakodkar"
-__email__ = "cacodcar@tamu.edu"
-__status__ = "Production"
diff --git a/src/energiapy/utils/data_utils.py b/src/energiapy/utils/data_utils.py
index bd6715ce..19ecac04 100644
--- a/src/energiapy/utils/data_utils.py
+++ b/src/energiapy/utils/data_utils.py
@@ -140,9 +140,8 @@ def make_f_purchase(location_list: list, day_list: list, hour_list: list, resour
     return f_purchase_dict_
 
 
-def make_henry_price_df(file_name: str, year: int, stretch: bool = False) -> pandas.DataFrame:
-    """makes a df from Henry Spot Price Index data
-    Days with missing data are filled using previous day values
+def make_henry_price_df(file_name: str, year: int, stretch: bool = False,) -> pandas.DataFrame:
+    """makes a df from data with missing data filled using previous day values
     The costs are converted to $/kg from $/MMBtu using a factor of /22.4
 
     Args:


Test patch:
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/test_process.py b/tests/test_process.py
new file mode 100644
index 00000000..1ea355e9
--- /dev/null
+++ b/tests/test_process.py
@@ -0,0 +1,30 @@
+from src.energiapy.components.resource import Resource
+from src.energiapy.components.process import Process, ProcessMode, CostDynamics
+
+def test_process_multiconv():
+    Resource1 = Resource(name = 'resource1')
+    Resource2 = Resource(name = 'resource2')    
+    Process_multi = Process(name = 'process_multi', multiconversion= {0: {Resource1: -1, Resource2:2}, 1:{Resource1: -1, Resource2:2.5}})
+    assert Process_multi.processmode == ProcessMode.multi
+
+
+def test_process_storage_mode():
+    Resource1 = Resource(name = 'resource1')  
+    Process_storage = Process(name = 'process_storage', storage= Resource1)
+    assert Process_storage.processmode == ProcessMode.storage
+    
+def test_process_storage_storemax(): 
+    Resource1 = Resource(name = 'resource1')  
+    Process_storage = Process(name = 'process_storage', storage= Resource1, store_max= 100)
+    assert Process_storage.resource_storage.store_max == 100
+    
+   
+def test_process_cost_dynamics(): 
+    Resource1 = Resource(name = 'resource1')  
+    Process_pwl = Process(name = 'process_storage', capex= {i: i+1 for i in range(4)})
+    assert Process_pwl.cost_dynamics == CostDynamics.pwl
+    
+
+    
+     
+
diff --git a/tests/test_temporal_scale.py b/tests/test_temporal_scale.py
new file mode 100644
index 00000000..4e20ddcc
--- /dev/null
+++ b/tests/test_temporal_scale.py
@@ -0,0 +1,15 @@
+from src.energiapy.components.temporal_scale import Temporal_scale
+
+
+scales = Temporal_scale(discretization_list= [i for i in range(4)])
+
+
+def test_temporal_scale_list(scales):
+    list_ = scales.list
+    assert list_ == [i for i in range(4)]
+    
+def test_temporal_scale_scale(scales):
+    scale = scales.scale
+    assert scale == {i: list(range(i+1)) for i in range(4)}
+    
+