Skip to content

Introduce new component from eesyplan#492

Draft
Bachibouzouk wants to merge 10 commits into
mainfrom
feature/introduce-new-component
Draft

Introduce new component from eesyplan#492
Bachibouzouk wants to merge 10 commits into
mainfrom
feature/introduce-new-component

Conversation

@Bachibouzouk

@Bachibouzouk Bachibouzouk commented Jul 8, 2026

Copy link
Copy Markdown

PROTOCOL (option a or b)

  1. a) write a new class which inherits from Asset with the new needed fields
    b) add the fields within the Asset class

  2. add an asset_type for this new component with their visible fields (under static/resources/assettypes_list.csv)

  3. add the asset to the drag and drop within the projects/views.py::scenario_create_topology function. Right at the beginning there is a dict named components with asset_type as keys and the verbose name as Value

  4. provide an icon for this component in static/asset/gui, its name must be <asset_type>.svg, like "commodity.svg" for example. You then need to add a mapping "<asset_type>:<asset_type>," within $component-categories-mapping in the static/scss/abstracts/_variables.scss

    At this stage the component will be visible within the GUI and can be dragged to the energy system. However option a) doesn't provide out of the box access to the component's fields, whereas option b) does.
    To change this one need to be able to set the model attribute of the Meta class to something else than Asset (ie a decorator function which would find the new asset's Model from a dict and return a CreateAssetForm instance with modified Meta

After internal disscussion and testing option a) seems the most long term beneficial, as it will make the components much more aligned on eesyplan structure. This PR already made the structural changes necessary for:

  • export a scenario to json and load it again within the GUI
  • export a scenario to a datapackage
  • duplicate a scenario using the GUI (clicking on the icon next to the scenario in the list of scenarios on the project display page)

Here is a list of the components which need to have their own Model (inheriting Asset class)

  • all the dso component (dso, gas_dso, h2_dso, heat_dso)
  • heat_pump (for this one the separate Model COPCalculator might need to be removed and added to the heat_pump interface)
  • chp
  • chp_fixed_ratio
  • electrolyzer

There is also the storage components (hess, h2ss, bess, gess), but the migration might be a bit more complex as they had a structure with a parent asset and 3 children assets

One need to look over clean() method from AssetCreateForm in forms.py to remove code which will not be necessary there anymore. Idem for the to_datapackage() method af the Asset class It will break the dto.py classes but this is only used for MVS and will be removed in the future.

Also look into widgets attribute of the Meta class attribute of the AssetCreateForm

Need to migrate the Storage into an eesyplan storage (this might require quite some preparation?)

@Bachibouzouk Bachibouzouk requested a review from paulapreuss July 8, 2026 13:11
Inherting from Asset might be preferable, as the class remain a child of
Asset, one can always use Asset.objects.filter(asset_type=...)
The ModelForm is instanciated with a Meta class referring to the
specific asset model if differing from Asset. Currently problems remain
at the export to datapackage and export scenarios
@Bachibouzouk Bachibouzouk force-pushed the feature/introduce-new-component branch from b3b2e5d to fd1cf09 Compare July 13, 2026 14:02
@Bachibouzouk

Copy link
Copy Markdown
Author

Now export of the scenario produces an error but only when the scenario is reimported and the special component clicked

The new asset specific fields are successfully exported
com = Commodity(asset_type=at, scenario=scen, full_load_hours_max=30)
com.save()
print(com.to_datapackage())
pdb.set_trace()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove before merge (linter is already flagging this anyway so shouldn't be a problem)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove the whole file before merge, this is just for testing

Comment on lines +1048 to +1050
asset_type = ASSET_MAPPING.get(self.asset_type.asset_type, Asset)
existing_asset = get_object_or_404(asset_type, unique_id=self.unique_id)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to do this lookup for the asset within the model class? isn't existing_asset just self?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, is this because Commodity is not treated as an Asset but as a separate class?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use self within the Asset's methods, then self is of type Asset and I had the problem that I couldn't list the extra attributes

# 2) add an asset_type for this new component with their visible fields (under static/resources/assettypes_list.csv)


class Commodity(Asset):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do like having the new component inherit from Asset rather than having a very bloated Asset class

Comment thread app/projects/forms.py


def get_asset_or_404(asset_type, asset_uuid):
asset_type = ASSET_MAPPING.get(asset_type, Asset)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
asset_type = ASSET_MAPPING.get(asset_type, Asset)
asset_model = ASSET_MAPPING.get(asset_type, Asset)

I would rename this to match the variable name also used in asset_form_factory

Comment thread app/projects/forms.py
widget=forms.HiddenInput(), required=False, label=""
)

if self.asset_type_name == "heat_pump":

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally if we split the assets into their own models it would be good to also handle these special cases within the asset models. Or if the parameters have different names anyway depending on the asset (coming from the eesyplan naming), could also be handled through the general widget definition.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would not need to handle this as special cases because here we are overloading "efficiency" and using it as many different things. If we simply inheritate from Asset, then we write cop as attribute of the heat pump and do not need this whole if statement block :)

Comment thread app/projects/forms.py
return _AssetCreateForm(asset_type=asset_type, **kwargs)


class AssetCreateForm(OpenPlanModelForm):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete if keeping the form factory

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just work in progress because of the nightmare StorageAssetForm which inherit from AssetForm. Hopefully we can get rid of the complicated StorageAsset with eesyplan :)

hess,energy_storage,Heat,storage,[name],kW (therm),"{'input_1':['bus_in_heat','Heat'],'output_1': ['bus_out_heat','Heat']}"
chp,energy_conversion,Electricity,extractionTurbineCHP,"[name,age_installed,installed_capacity,capex_fix,capex_var,opex_var,opex_fix,lifetime,optimize_cap,maximum_capacity,efficiency_multiple,efficiency,thermal_loss_rate]",kW,"{'input_1':['bus_in_fuel','Gas'],'output_1': ['bus_out_heat','Heat'], 'output_2': ['bus_out_electricity','Electricity']}"
chp_fixed_ratio,energy_conversion,Electricity,transformer,"[name,age_installed,installed_capacity,capex_var,opex_fix,lifetime,optimize_cap,maximum_capacity,efficiency_multiple,efficiency]",kW,"{'input_1':['bus_in_fuel','Gas'],'output_1': ['bus_out_heat','Heat'], 'output_2': ['bus_out_electricity','Electricity']}"
commodity,energy_production,Electricity,source,"[name,installed_capacity,capex_var,commodity_type,full_load_hours_max,full_load_hours_max_asset]",kW,"{'output_1': ['bus_out_electricity','Electricity']}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do we want to handle the commodity bus? Maybe we can just use the fuel bus for now?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is why I started to implement the commodity. A quick solution is to do the same as with the busses in OpenPlan: we get 4 busses to choose from and we could get 4 commodities to choose from.

And not always the default Asset class
Only the asset count of a given asset_type is relevant here
At those occasion it might be needed to make sure the classes inheriting
from Asset appear in the listing explicitely and not simply as Asset
instances.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants