Skip to content

Commit

Permalink
docs: fix header casings; add passive charging user guide
Browse files Browse the repository at this point in the history
  • Loading branch information
sstroemer authored Oct 29, 2024
1 parent 202fb63 commit a3a0bf7
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 29 deletions.
2 changes: 1 addition & 1 deletion docs/pages/user_guides/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Some of these user guides (those in the Q&A section of the docs' source, see `user_guides/qna`) are based on previous questions that users had, and discussions that occured to clarify. To simplify the process of converting these interactions into actually usable user guides for the future, one may apply a LLM of their choice. If doing that, the following base prompt might be helpful (**tip: hover your mouse & one-click copy in the top right**):

```text
You are a seasoned developer and work on energy system optimization models; write a short and concise tutorial / user-guide based on the interaction of a user with our support and Q&A team, that covers the discussed topic and addresses the initial problem or misunderstanding. Make sure to keep it short. Use simple and generally understandable wording. You can assume basic familiarity with programming, especially with Python. Output everything in markdown format. Start the tutorial with a proper short first-level header, followed by a "virtual question" that a user might ask themself. Base that question on the submitted information - no need to repeat any question directly verbatim. Remove all names from your answer if there are any.
You are a seasoned developer and work on energy system optimization models; write a short and concise tutorial / user-guide based on the interaction of a user with our support and Q&A team, that covers the discussed topic and addresses the initial problem or misunderstanding. Make sure to keep it short. Use simple and generally understandable wording. You can assume basic familiarity with programming, especially with Python. Output everything in markdown format. Start the tutorial with a proper short first-level header, followed by a "virtual question" that a user might ask themself. Base that question on the submitted information - no need to repeat any question directly verbatim. Remove all names from your answer if there are any. Header texts should start with an upper case letter, but use proper English case (mostly lower) for all other words.
Background information is available in the following documentation: https://ait-energy.github.io/iesopt/
Expand Down
6 changes: 3 additions & 3 deletions docs/pages/user_guides/qna/dynamic_marginal_costs.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Output-Dependent Marginal Costs
# Output-dependent marginal costs

> **Question:**
> How can I model a Unit where the marginal cost changes based on the level of energy output? For example, the cost is 5 €/MWh for outputs below 5 MW of power and 10 €/MWh for outputs above 5 MW of power.
Expand All @@ -10,12 +10,12 @@

### Explanation

1. **Low-Output Unit:**
1. **Low-output unit:**
- Represents the output up to 5 MW.
- Has a marginal cost of 5 €/MWh.
- Capacity is limited to 5 MW.

2. **High-Output Unit:**
2. **High-output unit:**
- Represents any output above 5 MW.
- Has a marginal cost of 10 €/MWh.
- No specific capacity limit unless there's an overall maximum output.
Expand Down
74 changes: 74 additions & 0 deletions docs/pages/user_guides/qna/passive_charging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Passive charging of a borehole heat storage

## Intro

> **Question:**
> How can I represent passive charging in a borehole heat storage within my energy system optimization model?
> **Answer:**
> It's complicated ... But the only generally applicable answer is: A custom addon. The underlying idea is to separate the passive charging from the storage, into a separate "artificial" Profile, that can then accurately depict the passive behavior.
## The problem

When modeling a borehole heat storage - a deep underground heat storage surrounded by warm soil - it's important to account for passive charging from the surrounding soil. This guide explains how to represent this passive energy input in your model.

### Common misconception

You might consider using the `state_percentage_loss` parameter with a negative value to simulate passive charging:

```yaml
state_percentage_loss: -0.01 # Attempting to charge 1% per timestep
```
However, `state_percentage_loss` is designed for losses based on the **current storage level**. Using a negative value would incorrectly charge the storage by a percentage of its **existing energy content**, not the energy it lacks.

Therefore, while `-0.01` might be an allowed or in other cases even reasonable choice, it's not made for what is needed here.

## Recommended solution

Instead, model passive charging as an additional input that depends on how much energy the storage lacks. Here's how:

### Naive static charging

Create a new component using a ranged Profile:

```yaml
passive_charging:
type: Profile
mode: ranged
lb: 0
ub: MAX_PASSIVE_POWER # Use any estimation of maximum passive charging power
```

### Custom constraint

Use an addon to add a constraint limiting the passive charging based on the storage's unfilled capacity. Assuming that `heat_storage` is the name of the stateful Node that represents the underground storage, then:

```julia
# ... other stuff in your addon ...
function add_passive_charging(model::JuMP.Model)
c_passive_charging = IESopt.get_component(model, "passive_charging")
c_storage = IESopt.get_component(model, "heat_storage")
IESopt.@constraint(model, [t in T], c_passive_charging.exp.value[t] <= 0.01 * (c_storage.state_ub - c_storage.var.state[t]))
end
# ... other stuff in your addon ...
```

This ensures the passive charging at time `t` doesn't exceed 1% of the storage's remaining capacity.

## Summary

### Steps to implement

1. **Create the storage component**: Define your underground storage Node without using `state_percentage_loss` for passive charging.
2. **Add the Passive Charging Input**: Introduce a Profile to represent the passive energy inflow.
3. **Set Profile Bounds**: Use the mode `ranged` for the Profile with appropriate lower (`lb`) and upper (`ub`) bounds.
4. **Add the Constraint**: Implement the constraint to tie the passive charging rate to the storage's unfilled capacity.
5. **Refine as Needed**: Adjust the constraint for more complex behaviors if necessary. There might be a lot (!) that you might want to specialize.

### Conclusion

By modeling passive charging as a constrained input energy flow based on the storage's remaining capacity, you accurately represent the thermal interactions of an underground heat storage with its environment.
8 changes: 4 additions & 4 deletions docs/pages/user_guides/qna/profiles_sign.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Sign Convention for Time Series
# Sign convention for time series

## Intro

Expand All @@ -24,7 +24,7 @@ If you connect your fixed Profile (`mode: fixed`, which is the default) to a Nod
- **Positive Values:** The Profile draws energy from the Node (consumption).
- **Negative Values:** The Profile effectively injects energy into the Node (generation).

### Why Does This Happen?
### Why does this happen?

In IESopt, drawing a negative amount of energy from a Node (`-x kWh`) is mathematically equivalent to injecting a positive amount of energy (`+x kWh`) into it. This means, the following are equal:

Expand All @@ -33,7 +33,7 @@ In IESopt, drawing a negative amount of energy from a Node (`-x kWh`) is mathema
- Injecting `x > 0` units of energy into a Node
- Withdrawing a negative amount of energy from a Node

## Practical Example
## Practical example

Suppose you have a fixed Profile connected to a heating grid Node:

Expand All @@ -49,7 +49,7 @@ myprofile:
- **At time step 2:** The profile injects with a power of 50 kW into the node (generation).
- **And so on.**
### Alternative Approach with `node_to`
### Alternative approach with `node_to`

If you prefer to handle injections explicitly, you can use `node_to`:

Expand Down
42 changes: 21 additions & 21 deletions docs/pages/user_guides/qna/snapshot_duration.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Time Resolution: Power vs. Energy
# Time resolution & power vs. energy

## Intro

Expand All @@ -10,30 +10,30 @@
## Details

### Keep Capacities in Power Units
### Keep capacities in power units

- **Unit Capacities:** Always express capacities in power units (e.g., kW), regardless of the time step duration.
- **Profiles and Connections:** Similarly, profiles (e.g., time series data) and connection bounds should remain in power units.

### Costs Are Per Unit of Energy
### Costs are per unit of energy

- **Variable Costs:** Input costs as monetary units per unit of energy (e.g., €/kWh).
- **Consistency:** This approach ensures that cost calculations remain accurate, as the model internally accounts for time step durations.

### Time Step Duration Is Handled Internally
### Time step duration is handled internally

- **Internal Calculations:** IESopt multiplies power by the duration of each time step to calculate energy.
- **Cost Formula:** The total cost is calculated using the formula:
- **Internal calculations:** IESopt multiplies power by the duration of each time step to calculate energy.
- **Cost formula:** The total cost is calculated using the formula:

$$
\text{Cost (€)} = \text{Power (kW)} \times \text{Duration (h)} \times \text{Cost per Energy Unit (€/kWh)}
$$

- **No Manual Adjustments Needed:** You don't need to convert capacities to energy units per time step; the model does this for you.
- **No manual adjustments deeded:** You don't need to convert capacities to energy units per time step; the model does this for you.

### Specify Time Step Duration in the Configuration
### Specify time step duration in the configuration

- **Snapshots Configuration:** Define the duration of each time step using the `weights` parameter in your `snapshots` configuration.
- **Snapshots configuration:** Define the duration of each time step using the `weights` parameter in your `snapshots` configuration.

```yaml
snapshots:
Expand All @@ -43,40 +43,40 @@
- **Example:** If each time step represents one day, setting `weights: 24` tells the model that each snapshot spans 24 hours.

### Storage Units Are in Energy Units
### Storage units are in energy units

- **Exception for Storage:** Capacities for storage units are specified in energy units (e.g., kWh) because they represent stored energy, not power output.
- **Exception for storage:** Capacities for storage units are specified in energy units (e.g., kWh) because they represent stored energy, not power output.

### Practical Example
### Practical example

Suppose you have a generator with:

- **Capacity:** 100 kW
- **Marginal Cost:** 0.10 €/kWh
- **Time Step Duration:** 24 hours (daily)
- **Marginal cost:** 0.10 €/kWh
- **Time step duration:** 24 hours (daily)

The model calculates the energy produced and the cost as follows:

- **Energy Produced per Time Step:**
- **Energy produced per time step:**

$$
\text{Energy (kWh)} = \text{Power (kW)} \times \text{Duration (h)} = 100 \times 24 = 2,400 \text{ kWh}
$$

- **Total Cost per Time Step:**
- **Total cost per time step:**

$$
\text{Cost (€)} = \text{Energy (kWh)} \times \text{Cost per Energy Unit (€/kWh)} = 2,400 \times 0.10 = 240 \text{ €}
$$

## Summary

### Key Takeaways
### Key takeaways

- **No Need to Adjust Units:** Keep capacities in power units; do not convert them to energy per time step.
- **Costs Remain per Energy Unit:** Continue to provide costs in €/kWh or similar units.
- **Model Handles Duration:** The model uses the `weights` parameter to account for the duration of each time step in calculations.
- **Consistency Is Crucial:** By keeping units consistent, you ensure accurate modeling results without additional adjustments.
- **No need to adjust units:** Keep capacities in power units; do not convert them to energy per time step.
- **Costs remain per energy unit:** Continue to provide costs in €/kWh or similar units.
- **Model handles duration:** The model uses the `weights` parameter to account for the duration of each time step in calculations.
- **Consistency is crucial:** By keeping units consistent, you ensure accurate modeling results without additional adjustments.

### Conclusion

Expand Down

0 comments on commit a3a0bf7

Please sign in to comment.