Source code for openalea.cnwgrass.hydraulics.postprocessing

"""
    hydraulics.postprocessing
    ~~~~~~~~~~~~~~~~~~~~~~

    The module :mod:`hydraulics.postprocessing` defines post-processing to apply
    on Turgor-Wheat outputs, and provides a front-end to automatize the generation of graphs
    for validation of the outputs.

    Please use front-ends :func:`postprocessing` and :func:`generate_graphs`.

"""

from __future__ import division  # use "//" to do integer division
import os

import pandas as pd
import matplotlib.pyplot as plt

from openalea.cnwgrass.hydraulics import simulation as hydraulics_simulation
from openalea.cnwgrass.hydraulics import parameters as hydraulics_parameters
from openalea.cnwgrass.integration import tools as integration_tools



#: the time index
T_INDEX = hydraulics_simulation.Simulation.T_INDEX

#: the index to locate the plants in the modelled system
PLANTS_INDEXES = hydraulics_simulation.Simulation.PLANTS_INDEXES
#: concatenation of :attr:`T_INDEX` and :attr:`PLANTS_INDEXES`
PLANTS_T_INDEXES = hydraulics_simulation.Simulation.PLANTS_T_INDEXES
#: plants post-processing variables
PLANTS_POSTPROCESSING_VARIABLES = []
#: concatenation of :attr:`PLANTS_T_INDEXES`, :attr:`PLANTS_RUN_VARIABLES <hydraulics.simulation.Simulation.PLANTS_RUN_VARIABLES>` and :attr:`PLANTS_POSTPROCESSING_VARIABLES`
PLANTS_RUN_POSTPROCESSING_VARIABLES = set(PLANTS_T_INDEXES + hydraulics_simulation.Simulation.PLANTS_RUN_VARIABLES + PLANTS_POSTPROCESSING_VARIABLES)

#: the indexes to locate the axes in the modelled system
AXES_INDEXES = hydraulics_simulation.Simulation.AXES_INDEXES
#: concatenation of :attr:`T_INDEX` and :attr:`AXES_INDEXES`
AXES_T_INDEXES = hydraulics_simulation.Simulation.AXES_T_INDEXES
#: axes post-processing variables
AXES_POSTPROCESSING_VARIABLES = []

#: concatenation of :attr:`AXES_T_INDEXES`, :attr:`AXES_RUN_VARIABLES <cnmetabolism.simulation.Simulation.AXES_RUN_VARIABLES>` and :attr:`AXES_POSTPROCESSING_VARIABLES`
AXES_RUN_POSTPROCESSING_VARIABLES = set(AXES_T_INDEXES + hydraulics_simulation.Simulation.AXES_RUN_VARIABLES + AXES_POSTPROCESSING_VARIABLES)

#: the indexes to locate the phytomers in the modelled system
PHYTOMERS_INDEXES = hydraulics_simulation.Simulation.PHYTOMERS_INDEXES
#: concatenation of :attr:`T_INDEX` and :attr:`PHYTOMERS_INDEXES`
PHYTOMERS_T_INDEXES = hydraulics_simulation.Simulation.PHYTOMERS_T_INDEXES
#: phytomers post-processing variables
PHYTOMERS_POSTPROCESSING_VARIABLES = []
#: concatenation of :attr:`PHYTOMERS_T_INDEXES`, :attr:`PHYTOMERS_RUN_VARIABLES <cnmetabolism.simulation.Simulation.PHYTOMERS_RUN_VARIABLES>` and :attr:`PHYTOMERS_POSTPROCESSING_VARIABLES`
PHYTOMERS_RUN_POSTPROCESSING_VARIABLES = set(PHYTOMERS_T_INDEXES + hydraulics_simulation.Simulation.PHYTOMERS_RUN_VARIABLES + PHYTOMERS_POSTPROCESSING_VARIABLES)

#: the indexes to locate the organs in the modelled system
ORGANS_INDEXES = hydraulics_simulation.Simulation.ORGANS_INDEXES
#: concatenation of :attr:`T_INDEX` and :attr:`ORGANS_INDEXES`
ORGANS_T_INDEXES = hydraulics_simulation.Simulation.ORGANS_T_INDEXES
#: organs post-processing variables
ORGANS_POSTPROCESSING_VARIABLES = []
ORGANS_RUN_VARIABLES_ADDITIONAL = []
#: concatenation of :attr:`ORGANS_T_INDEXES`, :attr:`ORGANS_RUN_VARIABLES <cnmetabolism.simulation.Simulation.ORGANS_RUN_VARIABLES>` and :attr:`ORGANS_POSTPROCESSING_VARIABLES`
ORGANS_RUN_POSTPROCESSING_VARIABLES = set(ORGANS_T_INDEXES + hydraulics_simulation.Simulation.ORGANS_RUN_VARIABLES + ORGANS_POSTPROCESSING_VARIABLES + ORGANS_RUN_VARIABLES_ADDITIONAL)

#: the indexes to locate the hidden zones in the modelled system
HIDDENZONE_INDEXES = hydraulics_simulation.Simulation.HIDDENZONE_INDEXES
#: concatenation of :attr:`T_INDEX` and :attr:`HIDDENZONE_INDEXES`
HIDDENZONE_T_INDEXES = hydraulics_simulation.Simulation.HIDDENZONE_T_INDEXES
#: hidden zones post-processing variables
HIDDENZONE_POSTPROCESSING_VARIABLES = ['conc_solutes_vol', 'conc_solutes_mass', 'LER']
HIDDENZONE_RUN_VARIABLES_ADDITIONAL = []
#: concatenation of :attr:`HIDDENZONE_T_INDEXES`, :attr:`HIDDENZONE_RUN_VARIABLES <hydraulics.simulation.Simulation.HIDDENZONE_RUN_VARIABLES>` and :attr:`HIDDENZONE_POSTPROCESSING_VARIABLES`
HIDDENZONE_RUN_POSTPROCESSING_VARIABLES = HIDDENZONE_T_INDEXES + hydraulics_simulation.Simulation.HIDDENZONE_RUN_VARIABLES + HIDDENZONE_RUN_VARIABLES_ADDITIONAL + HIDDENZONE_POSTPROCESSING_VARIABLES

#: the indexes to locate the elements in the modelled system
ELEMENTS_INDEXES = hydraulics_simulation.Simulation.ELEMENTS_INDEXES
#: concatenation of :attr:`T_INDEX` and :attr:`ELEMENTS_INDEXES`
ELEMENTS_T_INDEXES = hydraulics_simulation.Simulation.ELEMENTS_T_INDEXES
#: elements post-processing variables
ELEMENTS_POSTPROCESSING_VARIABLES = []
ELEMENTS_RUN_VARIABLES_ADDITIONAL = []
#: concatenation of :attr:`ELEMENTS_T_INDEXES`, :attr:`ELEMENTS_RUN_VARIABLES <hydraulics.simulation.Simulation.ELEMENTS_RUN_VARIABLES>` and :attr:`ELEMENTS_POSTPROCESSING_VARIABLES`
ELEMENTS_RUN_POSTPROCESSING_VARIABLES = ELEMENTS_T_INDEXES + hydraulics_simulation.Simulation.ELEMENTS_RUN_VARIABLES + ELEMENTS_RUN_VARIABLES_ADDITIONAL + ELEMENTS_POSTPROCESSING_VARIABLES

#: the indexes to locate the soils in the modelled system
SOILS_INDEXES = hydraulics_simulation.Simulation.SOILS_INDEXES
#: concatenation of :attr:`T_INDEX` and :attr:`SOILS_INDEXES`
SOILS_T_INDEXES = hydraulics_simulation.Simulation.SOILS_T_INDEXES
#: soils post-processing variables
SOILS_POSTPROCESSING_VARIABLES = []
#: concatenation of :attr:`SOILS_T_INDEXES`, :attr:`SOILS_RUN_VARIABLES <cnmetabolism.simulation.Simulation.SOILS_RUN_VARIABLES>` and :attr:`SOILS_POSTPROCESSING_VARIABLES`
SOILS_RUN_POSTPROCESSING_VARIABLES = SOILS_T_INDEXES + hydraulics_simulation.Simulation.SOILS_RUN_VARIABLES + SOILS_POSTPROCESSING_VARIABLES

# ---------------------------------------------------
#           POST-PROCESSING FUNCTIONS               -
#           DO NOT USE THEM DIRECTLY                -
# ---------------------------------------------------

[docs] class Roots: """ Post-processing to apply on Xylem outputs. """ pass
[docs] class Xylem: """ Post-processing to apply on Xylem outputs. """ pass
[docs] class HiddenZone: """ Post-processing to apply on HiddenZone outputs. """ def __init__(self): pass
[docs] @staticmethod def calculate_LER(leaf_L, init_leaf_L, delta_t): """Calculates Leaf elongation rate in phase II :param float leaf_L: Leaf length at time t (m) :param float init_leaf_L: Leaf length at time t-1 (m) :param float delta_t: Delta time (h) :return: Leaf elongation rate (mm h-1) :rtype: float """ LER = ((leaf_L - init_leaf_L) * 1E03) / delta_t return LER
[docs] @staticmethod def calculate_conc_solutes_vol(sucrose, fructan, amino_acids, volume): """Volumic concentration of solutes used for osmotic water potential calculation :param float sucrose: Amount of sucrose (µmol` C) :param float amino_acids: Amount of amino acids (µmol` N) :param float fructan: Amount of fructan (µmol` C) :param float volume: Volume (m3) :return: Solutes volumic concentration (mol m-3) :rtype: float """ conc_solutes_vol = (sucrose / hydraulics_parameters.NB_C_SUCROSE + fructan / hydraulics_parameters.NB_C_SUCROSE + amino_acids / hydraulics_parameters.AMINO_ACIDS_N_RATIO) * 1E-06 / (volume * hydraulics_parameters.VSTORAGE) return conc_solutes_vol
[docs] @staticmethod def calculate_conc_solutes_mass(fructan, sucrose, amino_acids, mstruct): """Massic concentration of solutes used for osmotic water potential calculation :param float sucrose: Amount of sucrose (µmol` C) :param float amino_acids: Amount of amino acids (µmol` N) :param float fructan: Amount of fructan (µmol` C) :param float mstruct: Structural mass (g) :return: Solutes massic concentration (mol m-3) :rtype: float """ conc_solutes_mass = (fructan + sucrose + amino_acids) * 1E-06 / mstruct return conc_solutes_mass
[docs] class Element: """ Post-processing to apply on Element outputs. """ pass
[docs] class Organ: """ Post-processing to apply on Organ outputs. """ pass
# ------------------------------------------------------------------------------------------------ # POST-PROCESSING FRONT-END - # PLEASE USE THIS FUNCTION TO APPLY POST-PROCESSING ON THE OUTPUT OF HYDRAULICS - # ------------------------------------------------------------------------------------------------
[docs] def postprocessing(plants_df=None, axes_df=None, metamers_df=None, hiddenzones_df=None, organs_df=None, elements_df=None, soils_df=None, delta_t=1): """ Compute post-processing from Hydraulics outputs, and format the post-processing to :class:`dataframes <pandas.DataFrame>`. For each post-processing output dataframe: * compute post-processing from Hydraulics outputs, * concatenate Hydraulics outputs and post-processing and place the results in a jointed dataframe, * reorder the columns of the dataframes according to :attr:`HIDDENZONE_RUN_POSTPROCESSING_VARIABLES`, :attr:`ELEMENTS_RUN_POSTPROCESSING_VARIABLES`, :attr:`ORGAN_RUN_POSTPROCESSING_VARIABLES` * and convert the indexes of plants and metamers to integers (if relevant). :param pandas.DataFrame plants_df: Hydraulics outputs at plant scale (see :attr:`simulation.Simulation.PLANTS_RUN_VARIABLES`) :param pandas.DataFrame axes_df: Hydraulics outputs at axis scale (see :attr:`simulation.Simulation.AXES_RUN_VARIABLES`) :param pandas.DataFrame metamers_df: Hydraulics outputs at phytomer scale (see :attr:`simulation.Simulation.PHYTOMERS_RUN_VARIABLES`) :param pandas.DataFrame hiddenzones_df: Hydraulics outputs at hidden zone scale (see :attr:`simulation.Simulation.HIDDENZONE_RUN_VARIABLES`) :param pandas.DataFrame elements_df: Hydraulics outputs at element scale (see :attr:`simulation.Simulation.ELEMENTS_RUN_VARIABLES`) :param pandas.DataFrame organs_df: Hydraulics outputs at xylem scale (see :attr:`simulation.Simulation. ORGAN_RUN_VARIABLES`) :param pandas.DataFrame soils_df: Hydraulics outputs at soil scale (see :attr:`simulation.Simulation.SOILS_RUN_VARIABLES`) :param float delta_t: Delta t between 2 outputs (in seconds). :return: :class:`dataframes <pandas.DataFrame>` of post-processing for each scale: * hidden zone (see :attr:`HIDDENZONE_RUN_POSTPROCESSING_VARIABLES`) * element (see :attr:`ELEMENTS_RUN_POSTPROCESSING_VARIABLES`) * xylem (see :attr:`XYLEM_RUN_POSTPROCESSING_VARIABLES`) * and soil (see :attr:`SOILS_RUN_POSTPROCESSING_VARIABLES`) :rtype tuple [pandas.DataFrame] """ returned_dataframes = [] # plants if plants_df is not None: pp_plants_df = pd.concat([plants_df, pd.DataFrame(columns=PLANTS_POSTPROCESSING_VARIABLES)], sort=False) pp_plants_df = pp_plants_df.reindex(PLANTS_RUN_POSTPROCESSING_VARIABLES, axis=1, copy=False) pp_plants_df['plant'] = pp_plants_df['plant'].astype(int) returned_dataframes.append(pp_plants_df) else: returned_dataframes.append(pd.DataFrame({'A': []})) # axes if axes_df is not None: axes_df = axes_df[axes_df['axis'] == 'MS'].copy() # TODO : Temporary ! pp_axes_df = pd.concat([axes_df, pd.DataFrame(columns=AXES_POSTPROCESSING_VARIABLES)], sort=False) pp_axes_df = pp_axes_df.reindex(AXES_RUN_POSTPROCESSING_VARIABLES, axis=1, copy=False) pp_axes_df.loc[:, 'plant_WC_DM'] = axes_df['plant_water_content'] / (axes_df['plant_water_content'] + axes_df['mstruct']) # pp_axes_df['plant'] = pp_axes_df['plant'].astype(int) returned_dataframes.append(pp_axes_df) # metamers if metamers_df is not None: pp_metamers_df = pd.concat([metamers_df, pd.DataFrame(columns=PHYTOMERS_POSTPROCESSING_VARIABLES)], sort=False) pp_metamers_df = pp_metamers_df.reindex(PHYTOMERS_RUN_POSTPROCESSING_VARIABLES, axis=1, copy=False) pp_metamers_df[['plant', 'metamer']] = pp_metamers_df[['plant', 'metamer']].astype(int) returned_dataframes.append(pp_metamers_df) else: returned_dataframes.append(pd.DataFrame({'A': []})) # hidden zones if hiddenzones_df is not None: hiddenzones_df.loc[:, 'conc_solutes_mass'] = HiddenZone.calculate_conc_solutes_mass(hiddenzones_df['fructan'], hiddenzones_df['sucrose'], hiddenzones_df['amino_acids'], hiddenzones_df['mstruct']) hiddenzones_df.loc[:, 'conc_solutes_vol'] = HiddenZone.calculate_conc_solutes_vol(hiddenzones_df['fructan'], hiddenzones_df['sucrose'], hiddenzones_df['amino_acids'], hiddenzones_df['volume']) hiddenzones_df.loc[:, 'LER'] = HiddenZone.calculate_LER(hiddenzones_df['leaf_L'], hiddenzones_df['init_leaf_L'], delta_t) pp_hiddenzones_df = pd.concat([hiddenzones_df, pd.DataFrame(columns=HIDDENZONE_POSTPROCESSING_VARIABLES)], sort=False) pp_hiddenzones_df = pp_hiddenzones_df.reindex(columns=HIDDENZONE_RUN_POSTPROCESSING_VARIABLES, copy=False) pp_hiddenzones_df[['plant', 'metamer']] = pp_hiddenzones_df[['plant', 'metamer']].astype(int) returned_dataframes.append(pp_hiddenzones_df) # elements if elements_df is not None: pp_elements_df = pd.concat([elements_df, pd.DataFrame(columns=ELEMENTS_POSTPROCESSING_VARIABLES)], sort=True) pp_elements_df = pp_elements_df.reindex(columns=ELEMENTS_RUN_POSTPROCESSING_VARIABLES, copy=False) grouped = elements_df.groupby('organ') for organ_type, parameters_class in (('blade', hydraulics_parameters.LAMINA_ELEMENT_PARAMETERS), ('internode', hydraulics_parameters.INTERNODE_ELEMENT_PARAMETERS), ('sheath', hydraulics_parameters.SHEATH_ELEMENT_PARAMETERS)): if organ_type not in grouped.groups: continue group = grouped.get_group(organ_type) if len(group) == 0: # TODO: faire mm tri que dans simulation de cnmetabolism (surface nulle) continue pp_elements_df = pp_elements_df.reindex(columns=ELEMENTS_RUN_POSTPROCESSING_VARIABLES, copy=False) pp_elements_df[['plant', 'metamer']] = pp_elements_df[['plant', 'metamer']].astype(int) returned_dataframes.append(pp_elements_df) # organs if organs_df is not None and axes_df is not None: pp_organs_df = pd.concat([organs_df, pd.DataFrame(columns=ORGANS_POSTPROCESSING_VARIABLES)], sort=False) pp_organs_df = pp_organs_df.reindex(columns=ORGANS_RUN_POSTPROCESSING_VARIABLES, copy=False) pp_organs_df['plant'] = pp_organs_df['plant'].astype(int) returned_dataframes.append(pp_organs_df) # soils if soils_df is not None: pp_soils_df = pd.concat([soils_df, pd.DataFrame(columns=SOILS_POSTPROCESSING_VARIABLES)]) pp_soils_df = pp_soils_df.reindex(columns=SOILS_RUN_POSTPROCESSING_VARIABLES, copy=False) pp_soils_df[['plant']] = pp_soils_df[['plant']].astype(int) returned_dataframes.append(pp_soils_df) else: returned_dataframes.append(pd.DataFrame({'A': []})) return tuple(returned_dataframes)
# --------------------------------------------------------------------- # GRAPHS GENERATION FRONT-END - # PLEASE USE THIS FUNCTION FOR THE GENERATION OF GRAPHS - # ---------------------------------------------------------------------
[docs] def generate_graphs(axes_df=None, hiddenzones_df=None, organs_df=None, elements_df=None, soils_df=None, meteo_data=None, graphs_dirpath='.'): """ Generate graphs to validate the outputs of Hydraulics, and save them in directory `graphs_dirpath`. :param pandas.DataFrame axes_df: Hydraulics outputs and post-processing at axis scale (see :attr:`PLANTS_RUN_POSTPROCESSING_VARIABLES`) :param pandas.DataFrame hiddenzones_df: Hydraulics outputs at hidden zone scale (see :attr:`HIDDENZONE_RUN_POSTPROCESSING_VARIABLES`) :param pandas.DataFrame elements_df: Hydraulics outputs at element scale (see :attr:`ELEMENTS_RUN_POSTPROCESSING_VARIABLES`) :param pandas.DataFrame organs_df: Hydraulics outputs at organ scale (see :attr:`ORGANS_RUN_POSTPROCESSING_VARIABLES`) :param pandas.DataFrame soils_df: Hydraulics outputs at soil scale (see :attr:`SOILS_RUN_POSTPROCESSING_VARIABLES`) :param pandas.DataFrame meteo_data: the meteo dataframe having the mapping between t (hours) and calendar dates :param str graphs_dirpath: the path of the directory to save the generated graphs """ x_name = 't' x_label = 'Time (Hour)' colors = ['blue', 'darkorange', 'green', 'red', 'darkviolet', 'gold', 'magenta', 'brown', 'darkcyan', 'grey', 'lime'] colors = colors + colors + colors + colors + colors # 1) Photosynthetic organs if elements_df is not None: graph_variables_ph_elements = {'age': u'Age (°Cd)', 'organ_volume': 'Volume of organ based on dimensions (m3)', 'epsilon_volume': 'Volumetric elasticity (Mpa)', 'Total_Transpiration_turgor': u'Total transpiration of turgor model (g H2O)', 'osmotic_water_potential': u'Osmotic water potential (MPa)', 'thickness': u'Thickness (m)', 'water_potential': u'Total water potential (MPa)', 'turgor_water_potential': u'Turgor water potential (MPa)', 'water_content': u'Water content (g)', 'water_influx': u'Water flow from Xylem (g)', 'width': u'Width (m)', 'WC_mstruct': u'ratio WC_mstruct (%)', 'resistance': u'Resistance (MPa s g$^{-1}$)', 'volume': u'Volume (m3)'} for org_ph in (['blade'], ['sheath'], ['internode']): for variable_name, variable_label in graph_variables_ph_elements.items(): graph_name = variable_name + '_' + '_'.join(org_ph) + '.PNG' integration_tools.plot_outputs(elements_df, x_name=x_name, y_name=variable_name, x_label=x_label, y_label=variable_label, colors=[colors[i - 1] for i in elements_df.metamer.unique().tolist()], filters={'organ': org_ph, 'axis': 'MS'}, plot_filepath=os.path.join(graphs_dirpath, graph_name), meteo_data=meteo_data, explicit_label=False) # 2) Hidden zones if hiddenzones_df is not None: graph_variables_hiddenzones = {'delta_hiddenzone_dimensions_plastic': u'dl / dt plastic (mm h-1)', 'LER': u'LER in phase II (mm h-1)', 'conc_solutes_vol': u'conc_solutes_vol', 'conc_solutes_mass': u'conc_solutes_mass', 'length_hz_En': u'Length of HZ after En (m)', 'organ_volume': u'Volume of hz based on dimensions (m3)', 'phi_volume': u'Volumetric extensibility (MPa-1 h-1) ', 'epsilon_volume': u'Volumetric elasticity (Mpa)', 'leaf_pseudo_age': u'Leaf pseudo age (°Cd)', 'phi_length': u'Extensibility parameter for length (Mpa-1 h-1)', 'phi_width': u'Extensibility parameter for width (Mpa-1 h-1)', 'phi_thickness': u'Extensibility parameter for thickness (Mpa-1 h-1)', 'length': u'Length of hz (m)', 'hiddenzone_age': u'Age (s)', 'osmotic_water_potential': u'Osmotic water potential (MPa)', 'width': u'Width (m)', 'water_potential': u'Total water potential (MPa)', 'turgor_water_potential': u'Turgor water potential (MPa)', 'water_content': u'Water content (g)', 'water_influx': u'Water flow Xylem (g)', 'resistance': u'Resistance (MPa s g$^{-1}$)', 'thickness': u'Thickness (m)', 'volume': u'Volume (m3)', 'WC_mstruct': u'ratio WC_mstruct'} for variable_name, variable_label in graph_variables_hiddenzones.items(): graph_name = variable_name + '_hz' + '.PNG' integration_tools.plot_outputs(hiddenzones_df, x_name=x_name, y_name=variable_name, x_label=x_label, y_label=variable_label, colors=[colors[i - 1] for i in hiddenzones_df.metamer.unique().tolist()], filters={'plant': 1, 'axis': 'MS'}, plot_filepath=os.path.join(graphs_dirpath, graph_name), meteo_data=meteo_data, explicit_label=False) # 3) Roots and xylem if organs_df is not None: graph_variables_organs = {'water_potential': u'Total water potential (MPa)'} for org in (['roots'], ['xylem']): for variable_name, variable_label in graph_variables_organs.items(): graph_name = variable_name + '_' + '_'.join(org) + '.PNG' integration_tools.plot_outputs(organs_df, x_name=x_name, y_name=variable_name, x_label=x_label, y_label=variable_label, colors=['blue'], filters={'organ': org}, plot_filepath=os.path.join(graphs_dirpath, graph_name), meteo_data=meteo_data, explicit_label=False) # 4) Axes graph_variables_axes = {'Total_Transpiration_turgor': u'Total transpiration from turgor model (g H2O)', 'Growth': u'Growth (g H2O)', 'water_influx': u' Water flux from xylem to HZ and photosynthetic organs (g H2O)', 'plant_water_content': u'Total water content of the plant (g H2O)', 'plant_WC_DM': u'Ratio of plant water content per structural mass (%)'} for variable_name, variable_label in graph_variables_axes.items(): graph_name = variable_name + '_axis' + '.PNG' integration_tools.plot_outputs(axes_df, x_name=x_name, y_name=variable_name, x_label=x_label, y_label=variable_label, colors=['blue'], filters={'plant': 1, 'axis': 'MS'}, plot_filepath=os.path.join(graphs_dirpath, graph_name), meteo_data=meteo_data, explicit_label=False) # 5) Soil if soils_df is not None: _, (ax1) = plt.subplots(1) SRWC = soils_df['SRWC'] ax1.plot(soils_df['t'], SRWC) ax1.set_ylabel(u'SRWC') ax1.set_xlabel('Time (hour)') ax1.set_title = 'SRWC' ax1.set_ylim(bottom=0) plt.savefig(os.path.join(graphs_dirpath, 'SRWC.PNG'), format='PNG', bbox_inches='tight') plt.close() _, (ax1) = plt.subplots(1) water_potential = soils_df['water_potential'] ax1.plot(soils_df['t'], water_potential) ax1.set_ylabel(u'Soil water potential (MPa)') ax1.set_xlabel('Time (hour)') ax1.set_title = 'Soil water potential' #ax1.set_ylim(bottom=0) plt.savefig(os.path.join(graphs_dirpath, 'Soil water potential.PNG'), format='PNG', bbox_inches='tight') plt.close()