Skip to content

Commit

Permalink
Merge pull request #70 from oemof/feature/constraint-facade
Browse files Browse the repository at this point in the history
Load constraints from datapackage
  • Loading branch information
jnnr committed Feb 6, 2023
2 parents 457ea47 + bd4b98c commit 69eb4a3
Show file tree
Hide file tree
Showing 20 changed files with 701 additions and 38 deletions.
8 changes: 5 additions & 3 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ We adhere to the frictionless `(tabular) datapackage standard <https://friction
On top of that structure we add our own logic. We require at least two things:

1. A directory named *data* containing at least one sub-folder called *elements*
(optionally it may contain a directory *sequences* and *geometries*. Of
(optionally it may contain a directory *sequences*, *geometries* and/or *constraints*. Of
course you may add any other directory, data or other information.)

2. A valid meta-data `.json` file for the datapackage
Expand Down Expand Up @@ -143,8 +143,10 @@ resources are equivalent to parameters of the energy system elements and
sequences.

To distinguish elements and sequences these two are stored in sub-directories of
the data directory. In addition geometrical information can be stored under
`data/geometries` in a `.geojson` format. To simplifiy the process of creating
the data directory. In addition, geometrical information can be stored under
`data/geometries` in a `.geojson` format. An optional subdirectory `data/constraints`
can hold data describing global constraints.
To simplifiy the process of creating
and processing a datapackage you may
also use the funtionalities of the :py:mod:`~oemof.tabular.datapackage`

Expand Down
42 changes: 42 additions & 0 deletions src/oemof/tabular/constraint_facades.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import abc
from dataclasses import dataclass

from oemof.solph.constraints.integral_limit import generic_integral_limit


class ConstraintFacade(abc.ABC):
def build_constraint(self):
pass


@dataclass
class GenericIntegralLimit(ConstraintFacade):
name: str
type: str
limit: float
keyword: str = "emission_factor"

def build_constraint(self, model):
# to use the constraints in oemof.solph, we need to pass the model.

# check if there are flows with key
flows = {}
for (i, o) in model.flows:
if hasattr(model.flows[i, o], self.keyword):
flows[(i, o)] = model.flows[i, o]

if not flows:
raise Warning(f"No flows with keyword {self.keyword}")
else:
print(
f"These flows will contribute to the "
f"emission constraint {flows.keys()}"
)

# add constraint to the model
generic_integral_limit(
model, keyword=self.keyword, flows=flows, limit=self.limit
)


CONSTRAINT_TYPE_MAP = {"generic_integral_limit": GenericIntegralLimit}
5 changes: 4 additions & 1 deletion src/oemof/tabular/datapackage/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from oemof.network.energy_system import EnergySystem
from oemof.solph import Model

from . import building # noqa F401
from .reading import deserialize_energy_system
from .reading import deserialize_constraints, deserialize_energy_system

EnergySystem.from_datapackage = classmethod(deserialize_energy_system)

Model.add_constraints_from_datapackage = deserialize_constraints
20 changes: 20 additions & 0 deletions src/oemof/tabular/datapackage/building.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ def infer_metadata(
)
p.add_resource(r.descriptor)

# create meta data resources geometries
if not os.path.exists("data/geometries"):
print(
"No geometries path found in directory {}. Skipping...".format(
Expand All @@ -192,6 +193,25 @@ def infer_metadata(
)
p.add_resource(r.descriptor)

# create meta data resources constraints
if not os.path.exists("data/constraints"):
print(
"No constraints path found in directory {}. Skipping...".format(
os.getcwd()
)
)
else:
for f in os.listdir("data/constraints"):
r = Resource(
{"path": str(pathlib.PurePosixPath("data", "constraints", f))}
)
r.infer()
r.commit()
r.save(
pathlib.PurePosixPath("resources", f.replace(".csv", ".json"))
)
p.add_resource(r.descriptor)

p.commit()
p.save(metadata_filename)

Expand Down
37 changes: 37 additions & 0 deletions src/oemof/tabular/datapackage/reading.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,3 +481,40 @@ def find(n, d):

else:
raise ValueError("Timeindices in resources differ!")


def deserialize_constraints(model, path, constraint_type_map=None):
if constraint_type_map is None:
constraint_type_map = {}

def listify(x, n=None):
return (
x if isinstance(x, list) else repeat(x) if not n else repeat(x, n)
)

package = dp.Package(path)

# read all resources in data/constraints
resources = []
for r in package.resources:
if all(
re.match(r"^data/constraints/.*$", p)
for p in listify(r.descriptor["path"], 1)
):
resources.append(r)

for resource in resources:

resource_data = resource.read(keyed=True, relations=True)

for rw in resource_data:
constraint_type = rw["type"]

constraint_facade = constraint_type_map[constraint_type]

constraint = constraint_facade(**rw)

# build constraint for each facade
constraint.build_constraint(model)

# return model
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Emission constraint example for oemof-tabular

Run `scripts/infer.py` from the datapackage root directory to add the
meta data file `datapackage.json` after updating the resources of the
datapackage.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
name;type;limit;keyword
constraint0;generic_integral_limit;1000;emission_factor
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
name;type;balanced
bus-electricity;bus;true
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
name;type;carrier;tech;capacity;bus;marginal_cost;profile;output_parameters
gas;dispatchable;gas;gt;1000;bus-electricity;40;1;{"emission_factor": 10}
coal;dispatchable;coal;st;1000;bus-electricity;40;1;{"emission_factor": 20}
lignite;dispatchable;lignite;st;500;bus-electricity;20;1;{"emission_factor": 30}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
name;type;bus
electricity-excess;excess;bus-electricity
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
name;amount;profile;type;bus
electricity-demand;5000;electricity-load-profile;load;bus-electricity
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
name,carrier,tech,storage_capacity,capacity,capacity_cost,storage_capacity_initial,type,bus
el-storage,lithium,battery,100,10,10,0.5,storage,bus-electricity
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
name;type;carrier;tech;capacity;capacity_cost;bus;marginal_cost;profile;output_parameters
wind;volatile;wind;onshore;50;;bus-electricity;0;wind-profile;{}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
timeindex,electricity-load-profile
2011-01-01T00:00:00Z,0.000745659236
2011-01-01T01:00:00Z,0.000709651546
2011-01-01T02:00:00Z,0.00068564642
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
timeindex,wind-profile
2011-01-01T00:00:00Z,0.147532
2011-01-01T01:00:00Z,0.184181
2011-01-01T02:00:00Z,0.223937

0 comments on commit 69eb4a3

Please sign in to comment.