greenpeer / GreenLightPlus

GreenLightPlus is a ToolKit for greenhouse environment simulation and energy consumption-yield analysis. It combines the original GreenLight model with the EnergyPlus simulation tool to investigate the energy consumption and output of different greenhouse structures.
GNU General Public License v3.0
16 stars 3 forks source link

Simulation for loop not work for more than one iteration #3

Closed Atomao closed 3 months ago

Atomao commented 4 months ago

Hi, I was trying to run the provided code with different value for parameter season_interval = 5

from GreenLightPlus import (
    GreenLightModel,
    extract_last_value_from_nested_dict,
    calculate_energy_consumption,
    plot_green_light,
)

# Set simulation parameters
season_length = 10  # Length of growth cycle (days), can be set as a fraction
season_interval = 5  # Time interval for each model run (days), can be set as a fraction, e.g., 1/24/4 represents 15 minutes
first_day = 91  # First day of the growth cycle (day of the year)

# Create a GreenLight model instance
# Parameter explanation:
# - first_day: Start date of the simulation (day of the year)
# - isMature: Indicates whether the crop is mature
# - epw_path: Path to the weather data file
model = GreenLightModel(first_day=first_day, isMature=True, epw_path="NLD_Amsterdam.062400_IWEC.epw")

# Initialize cumulative variables
total_yield = 0  # Total yield (kg/m2)
lampIn = 0  # Lighting energy consumption (MJ/m2)
boilIn = 0  # Heating energy consumption (MJ/m2)

# Initialize model state and parameters
init_state = {
    "p": {
        # Greenhouse structure settings
        'psi': 22,  # Average slope of greenhouse cover (degrees)
        'aFlr': 4e4,  # Floor area (m^2)
        'aCov': 4.84e4,  # Cover area, including side walls (m^2)
        'hAir': 6.3,  # Height of main area (m) (ridge height is 6.5m, screen is 20cm below)
        'hGh': 6.905,  # Average greenhouse height (m)
        'aRoof': 0.1169*4e4,  # Maximum roof ventilation area (m^2)
        'hVent': 1.3,  # Vertical dimension of a single ventilation opening (m)
        'cDgh': 0.75,  # Discharge coefficient for ventilation (dimensionless)
        'lPipe': 1.25,  # Length of pipe-rail heating system (m/m^2)
        'phiExtCo2': 7.2e4*4e4/1.4e4,  # CO2 injection capacity for the entire greenhouse (mg/s)
        'pBoil': 300*4e4,  # Boiler capacity for the entire greenhouse (W)

        # Control settings
        'co2SpDay': 1000,  # CO2 setpoint during light period (ppm)
        'tSpNight': 18.5,  # Temperature setpoint during dark period (°C)
        'tSpDay': 19.5,  # Temperature setpoint during light period (°C)
        'rhMax': 87,  # Maximum relative humidity (%)
        'ventHeatPband': 4,  # P-band for ventilation at high temperature (°C)
        'ventRhPband': 50,  # P-band for ventilation at high relative humidity (% humidity)
        'thScrRhPband': 10,  # P-band for screen opening at high relative humidity (% humidity)
        'lampsOn': 0,  # Time to turn on lights (h)
        'lampsOff': 18,  # Time to turn off lights (h)
        'lampsOffSun': 400,  # Global radiation above which lamps are turned off (W/m^2)
        'lampRadSumLimit': 10  # Predicted daily sum of solar radiation below which lamps are used (MJ/m^2/day)
    }
}

# Run the model based on growth cycle and time interval
for current_step in range(int(season_length // season_interval)):
    # Run the model and get results
    gl = model.run_model(gl_params=init_state, season_length=season_length,
                         season_interval=season_interval, step=current_step)
    init_state = gl
    dmc = 0.06  # Dry matter content

    # Calculate and print current yield (kg/m2)
    current_yield = 1e-6 * calculate_energy_consumption(gl, 'mcFruitHar') / dmc
    print(f"Current yield: {current_yield:.2f} kg/m2")

    # Accumulate fruit yield (kg/m2)
    total_yield += current_yield

    # Calculate and accumulate energy consumption from lighting and heating (MJ/m2)
    lampIn += 1e-6 * calculate_energy_consumption(gl, "qLampIn", "qIntLampIn")
    boilIn += 1e-6 * calculate_energy_consumption(gl, "hBoilPipe", "hBoilGroPipe")

# Print final results
print(f"Total yield: {total_yield:.2f} kg/m2")
print(f"Lighting energy consumption: {lampIn:.2f} MJ/m2")
print(f"Heating energy consumption: {boilIn:.2f} MJ/m2")
print(f"Energy consumption per unit: {(lampIn + boilIn)/total_yield:.2f} MJ/kg")

# Plot model results
plot_green_light(gl)

I got an error like this and need help to fix it. The error I got on second iteration of simulation for loop and from what I can understand on first iter everything works because data_dict is a dict with matrices(np.ndarray) values, but on second iteration the corresponding matrices are just decimal numbers.

---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
Cell In[1], line 59
     56 # Run the model based on growth cycle and time interval
     57 for current_step in range(int(season_length // season_interval)):
     58     # Run the model and get results
---> 59     gl = model.run_model(gl_params=init_state, season_length=season_length,
     60                          season_interval=season_interval, step=current_step)
     61     init_state = gl
     62     dmc = 0.06  # Dry matter content

File ~/Desktop/greenhouse_sim/greenlightplus/GreenLightPlus/core/green_light_model.py:305, in GreenLightModel.run_model(self, gl_params, season_length, season_interval, step, start_row, end_row, time_step)
    302 solver = "BDF"  # Backward Differentiation Formula
    304 # Create an instance of ODESolver
--> 305 solver_instance = ODESolver(self.u, self.gl)
    306 self.solver_instance = solver_instance
    308 # Solve the differential equations and generate the results

File [~/Desktop/greenhouse_sim/greenlightplus/GreenLightPlus/create_green_light_model/ode.py:36](http://localhost:8888/lab/tree/greenlightplus/greenlightplus/GreenLightPlus/create_green_light_model/ode.py#line=35), in ODESolver.__init__(self, u, gl)
     30 """
     31 Initialize the ODESolver class
     32 :param u: Control variable matrix
     33 :param gl: GreenLight model instance
     34 """
     35 self.gl = gl  # Store the entire GreenLight model instance
---> 36 self.d = self.convert_dict_to_array(self.gl['d']).copy()  # Convert 'd' dict to array and copy
     37 self.u = u  # Store the control variable matrix
     38 self.prev_gl = {}

File [~/Desktop/greenhouse_sim/greenlightplus/GreenLightPlus/create_green_light_model/ode.py:47](http://localhost:8888/lab/tree/greenlightplus/greenlightplus/GreenLightPlus/create_green_light_model/ode.py#line=46), in ODESolver.convert_dict_to_array(self, data_dict)
     41 """
     42 Convert dictionary data to a 2D NumPy array
     43 :param data_dict: Dictionary containing the data
     44 :return: Converted 2D NumPy array
     45 """
     46 keys = list(data_dict.keys())  # Get all keys of the dictionary
---> 47 num_rows = data_dict[keys[0]].shape[0]  # Get number of rows from the first array
     49 # Check if all arrays have the same number of rows
     50 for key in keys:

IndexError: tuple index out of range
gsoykan commented 4 months ago

I think this is a fundamental issue with the codebase that affects the run_model function in the GreenLightModel class. Here's a detailed breakdown of the problem:

  1. Initial Run:

    • The run_model function is called with gl_params that do not contain the 'd' dictionary.
    • Therefore, the line:
      gl_params = extract_last_value_from_nested_dict(gl_params)

      which converts the 2D ndarrays in the gl_params dictionary to 1D ndarrays, does not affect 'd' as it is not present.

  2. Subsequent Runs:

    • In subsequent runs (triggered by the callback: on_end_of_zone_timestep_after_zone_reporting), gl_params includes the 'd' dictionary with string keys and 2D np.ndarray values.
    • The extract_last_value_from_nested_dict function now affects 'd', resulting in float values instead of ndarrays. This conversion causes issues because:
      self.d = self.convert_dict_to_array(self.gl['d']).copy()

      fails to initialize ODESolver correctly, as its variable depends on 'd' being an ndarray.

  3. Impact:

    • The convert_dict_to_array method cannot process float values and retrieve their shapes, leading to errors.

Could the author, @greenpeer, please address this issue or provide guidance on how to manage the effects of this inconsistency? Thank you!

greenpeer commented 3 months ago

@Atomao Thank you for raising this issue. @gsoykan Thank you for your detailed analysis.

This issue has been resolved.

The problem was addressed through modifications to the ODESolver class in the create_green_light_model/ode.py file. The following changes were implemented:

  1. A new parameter d was added to the ODESolver class constructor.
  2. The line self.d = self.convert_dict_to_array(self.gl['d']).copy() was removed.
  3. It was replaced with self.d = d # Store the entire uncontrolled variables matrix.

These adjustments prevent the issue where 2D arrays were being converted to float values.

To utilize this fix, the latest version of the repository should be pulled. The simulation should now run without encountering the previous error.

Atomao commented 3 months ago

Hi, tested new version and current error not appeared so close issue.