Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion grassland/parameters.csv
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,6 @@ Vmax_Km_int,0
Uptake_Vmax_Km,0.2
Uptake_Vmax_Km_int,0
Km_error,0
Specif_factor,1
Specif_factor,1
R_moist_decay,0.05
R_moist_uptake,0.1
4 changes: 3 additions & 1 deletion grassland/scrubland/parameters.csv
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,6 @@ Vmax_Km_int,0
Uptake_Vmax_Km,0.2
Uptake_Vmax_Km_int,0
Km_error,0
Specif_factor,1
Specif_factor,1
R_moist_decay,0.05
R_moist_uptake,0.1
4 changes: 3 additions & 1 deletion input/parameters.csv
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,6 @@ Vmax_Km_int,0
Uptake_Vmax_Km,0.2
Uptake_Vmax_Km_int,0
Km_error,0
Specif_factor,1
Specif_factor,1
R_moist_decay,0.05
R_moist_uptake,0.1
3 changes: 2 additions & 1 deletion input/runtime.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ Input_NH4 0
Input_PO4 0
direct 0.95
dist 1
interval 1
interval 1
output_CSV 1
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ requires-python = ">=3.12"
dependencies = [
"pandas>=2.2.3",
"scipy>=1.15.2",
"xarray>=2025.12.0"
]

[dependency-groups]
Expand Down
6 changes: 5 additions & 1 deletion src/dementpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def main():
pulse = int(runtime.loc['pulse',1]) # number of pulses
cycle = int(runtime.loc['end_time',1]) # number of time steps in each pulse
interval = int(runtime.loc['interval',1]) # interval of time step to record outputs
Export_format = int(runtime.loc['output_CSV',1]) # interval of time step to record outputs
mode = int(runtime.loc['dispersal',1]) # 0:'default' or 1:'dispersal'

#...Initialize data by calling the Function: Initialize_Data()
Expand Down Expand Up @@ -101,7 +102,10 @@ def main():

#...export the Output_init object to the output_folder using the export() funtion in the utility module
os.chdir('../'+output_folder)
export(Output_init, site, outname)
if Export_format == 1:
Output_init.export_to_csv(outname)
else:
Output_init.export_to_netcdf(outname)

if __name__ == '__main__':
main()
9 changes: 6 additions & 3 deletions src/grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ def __init__(self,runtime,data_init):
self.Km0 = data_init['Km0'] # Half-saturation constant
self.SubstrateRatios = np.float32('nan') # Substrate stoichiometry
self.DecayRates = np.float32('nan') # Substrate decay rate
self.R_moist_decay = data_init['R_moist_decay'] # Rate for change of moisture reaction in decay module

#Uptake
#self.Microbes_init = data_init['Microbes_pp'] # microbial community before placement
Expand All @@ -75,7 +76,8 @@ def __init__(self,runtime,data_init):
self.Taxon_Uptake_C = np.float32('nan') # taxon uptake of C
self.Taxon_Uptake_N = np.float32('nan') # taxon uptake of N
self.Taxon_Uptake_P = np.float32('nan') # taxon uptake of P

self.R_moist_uptake = data_init['R_moist_uptake'] # Rate for change of moisture reaction in uptake module

#Metabolism
self.Consti_Enzyme_C = data_init["EnzProdConstit"] # C cost of encoding constitutive enzyme
self.Induci_Enzyme_C = data_init["EnzProdInduce"] # C Cost of encoding inducible enzyme
Expand Down Expand Up @@ -154,7 +156,7 @@ def degradation(self,day):
SubstrateRatios = SubstrateRatios.fillna(0) # NOTE:ensure NA(b/c of 0/0 in df) = 0

# Arrhenius equation for Vmax and Km multiplied by exponential decay for Psi sensitivity
Vmax = Arrhenius(self.Vmax0, self.Ea, self.temp[day]) * Allison(0.05, self.wp_fc, self.psi[day]) # Vmax: (enz*gridsize) * sub
Vmax = Arrhenius(self.Vmax0, self.Ea, self.temp[day]) * Allison(self.R_moist_decay, self.wp_fc, self.psi[day]) # Vmax: (enz*gridsize) * sub
Km = Arrhenius(self.Km0, self.Km_Ea, self.temp[day]) # Km: (sub*gridsize) * enz

# Multiply Vmax by enzyme concentration
Expand Down Expand Up @@ -231,7 +233,7 @@ def uptake(self,day):

# Start calculating monomer uptake
# Caculate uptake enzyme kinetic parameters, multiplied by moisture multiplier accounting for the diffusivity implications
Uptake_Vmax = Arrhenius(self.Uptake_Vmax0, self.Uptake_Ea, self.temp[day]) * Allison(0.1, self.wp_fc, self.psi[day])
Uptake_Vmax = Arrhenius(self.Uptake_Vmax0, self.Uptake_Ea, self.temp[day]) * Allison(self.R_moist_uptake, self.wp_fc, self.psi[day])
Uptake_Km = Arrhenius(self.Uptake_Km0, self.Km_Ea, self.temp[day])

# Equation for hypothetical potential uptake (per unit of compatible uptake protein)
Expand Down Expand Up @@ -715,6 +717,7 @@ def reinitialization(self,initialization,microbes_pp,output,mode,pulse,*args):
# last: assign microbes to each grid box randomly based on prior densities
choose_taxa = np.zeros((self.n_taxa,self.gridsize), dtype='int8')
for i in range(self.n_taxa):
choose_taxa[i,:] = np.random.binomial(1, frequencies[i], self.gridsize)
freq=np.float64(frequencies.iloc[1]) #working independent of indexing (based on location not index labels)
p_vec = np.array([freq, 1.0 - freq], dtype=np.float64) #solve numoy issues
p_vec = p_vec / p_vec.sum()
Expand Down
137 changes: 136 additions & 1 deletion src/initialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
"""
import pandas as pd
import numpy as np
import xarray as xr

import warnings
import numbers
from pathlib import Path
from typing import Union

from substrate import Substrate
from monomer import Monomer
Expand Down Expand Up @@ -159,8 +165,137 @@ def initialize_data(runtime_parameters, site):
'wp_fc': parameters.loc['wp_fc',1], # threshold below which microbes start to respond to drought
'wp_th': parameters.loc['wp_th',1], # threshold below which microbes in full swing to respond to drought
'alpha': parameters.loc['alpha',1], # factor delineating curve concavity of microbial response to drought
'R_moist_decay': parameters.loc['R_moist_decay',1], # factor in the Allison equation for moisture (Decay part)
'R_moist_uptake': parameters.loc['R_moist_uptake',1], # factor in the Allison equation for moisture (Uptake part)
'Temp': daily_temp, # temperature
'Psi': daily_psi # water potential
}

return Data_Dictionary
return Data_Dictionary


def export_initialization_dict_to_csv(base_path: Path | str, d: dict) -> None:
"""Export contents of the initialisation directory to a folder.

Writes each of the items of a type below to a separate CSV file
- pandas.DataFrame
- pandas.Series
- numpy.ndarray of rank below 2
All scalar numbers are grouped in a single CSV 'scalars.csv' file.

Note:
All other items are ignored following a warning!
If you need them written you need to add extra entry.
"""

# Create space for output
base_path = Path(base_path)
base_path.mkdir(parents=True, exist_ok=True)

# Collect all scalar numbers
scalar_numbers = dict()

for name, member in d.items():
if isinstance(member, (pd.DataFrame, pd.Series)):
fname = name + ".csv"
member.to_csv(base_path / fname)
elif isinstance(member, np.ndarray):
if len(member.shape) <= 2:
fname = name + ".csv"
np.savetxt(fname, member, delimiter=",")
else:
warnings.warn(
f"Member '{name}' of initialisation dictionary could not be saved since "
f"it is an array of rank higher than 2 (rank: {len(member.shape)})."
)
elif isinstance(member, numbers.Number):
scalar_numbers[name] = member
else:
warnings.warn(
f"Initialisation member '{name}' has unsupported type '{type(member)}'. "
f"It has not been exported to the output directory '{base_path}'."
)

# Print numbers
pd.Series(scalar_numbers).to_csv(base_path / "scalars.csv")

def export_to_netcdf(self, base_path: Path | str) -> None:
"""Export contents of the output file to a directory in NetCDF format.
- Each pandas.DataFrame member is saved to a separate .nc file.
- All pandas.Series members are combined and saved to a single 'series.nc' file.
- All scalar numerical members are grouped and saved to 'scalars.nc'.

Parameters:
base_path : Path
A path that names the root directory where contents will be exported.
If the directory does not exist it will be created.
"""
# Create space for output
base_path = Path(base_path)
base_path.mkdir(parents=True, exist_ok=True)

# Collect all series and scalar data
series_data = dict()
scalar_numbers = dict()

for name, member in vars(self).items():
# convert each DataFrame to an xarray Dataset and save to .nc
if isinstance(member, pd.DataFrame):
# Ensure column names are strings
member.columns = member.columns.astype(str)
if member.index.name is not None:
member.index.name = str(member.index.name)
fname = name + ".nc" # use the .nc extension
try:
xarray_member = xr.Dataset.from_dataframe(member)
xarray_member.to_netcdf(base_path / fname)
except Exception as e:
warnings.warn(
f"Could not export DataFrame '{name}' to NetCDF. Error: {e}"
)

elif isinstance(member, pd.Series):
series_data[name] = member

elif isinstance(member, numbers.Number):
scalar_numbers[name] = member

elif name == "Initialization":
# Special case - Initialization dictionary
# Serialise it to a subfolder
path = base_path / name
export_initialization_dict_to_netcdf(path, member)
elif isinstance(member, pd.Series):
xrmember = xr.DataArray(member)
fname = name + ".nc"
xrmember.to_netcdf(base_path / fname)

else:
warnings.warn(
f"Output member '{name}' has unsupported type '{type(member)}'. "
f"It has not been exported to the output directory '{base_path}'."
)

# process and save Series
if series_data:
try:
# Combine all Series into a single DataFrame.
combined_series_df = pd.concat(series_data, axis=1)
# Convert the combined DataFrame to an xarray Dataset.
series_dataset = xr.Dataset.from_dataframe(combined_series_df)
# Save the Series Dataset to a single NetCDF file.
series_dataset.to_netcdf(base_path / "series.nc")
except ValueError as e:
# This handles the "duplicate labels" error if it occurs.
warnings.warn(
f"Could not export combined series due to an error: {e}. "
"Consider cleaning the index of your Series data first."
)

if scalar_numbers:
# Create an xarray Dataset directly from the dictionary of scalars.
# Each key will become a variable in the NetCDF file.
scalars_dataset = xr.Dataset(scalar_numbers)
# Save the scalars Dataset to a NetCDF file.
scalars_dataset.to_netcdf(base_path / "scalars.nc")

4 changes: 2 additions & 2 deletions src/microbe.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ def microbial_community_initialization(self,microbes_pp,fb_grid):
pb = self.taxa_per_box
choose_taxa = np.random.choice([1,0], self.n_taxa*self.gridsize,replace=True, p=[pb,(1-pb)])
pf = pb * self.max_size_b/self.max_size_f
choose_taxa[fb_grid==1] = np.random.choice([1,0], sum(fb_grid),replace=True, p=[pf,(1-pf)])
choose_taxa[fb_grid==1] = np.random.choice([1,0], np.sum(fb_grid),replace=True, p=[pf,(1-pf)])
microbes_df.loc[choose_taxa==0] = np.float32(0)


Expand Down Expand Up @@ -534,4 +534,4 @@ def microbe_osmo_psi(alfa,Psi_fc,Psi):
else:
f_osmo = 1 - alfa * Psi

return np.float32(f_osmo)
return np.float32(f_osmo)
Loading
Loading