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
Binary file modified docs/source/tutorials/GenVeg/GenVeg_Dune_Simulation.xlsx
Binary file not shown.
1 change: 1 addition & 0 deletions docs/source/user_guide/reference/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ following categories of components:

* {mod}`landlab.components.vegetation_dynamics`
* {mod}`landlab.components.plant_competition_ca`
* {mod}`landlab.components.genveg`

## Biota

Expand Down
8 changes: 8 additions & 0 deletions src/landlab/components/genveg/allometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ def calc_abg_dims(self, abg_biomass, cm=False):
return (basal_dia, shoot_width, height)

def calc_abg_biomass_from_dim(self, dim, var_name, cm=False):
# Use any dimension to estimate abg biomass
abg_biomass = np.zeros_like(dim)
filter = np.nonzero(dim > self.morph_params[var_name]["min"])
if cm is True:
Expand All @@ -102,6 +103,7 @@ def __init__(self, params, empirical_coeffs, cm=True):
super().__init__(params, empirical_coeffs, cm)

def calc_abg_dims(self, abg_biomass, cm=True):
# Calculate all abg dimensions from abg biomass
basal_dia = np.zeros_like(abg_biomass)
shoot_sys_width = np.zeros_like(abg_biomass)
height = np.zeros_like(abg_biomass)
Expand All @@ -116,6 +118,7 @@ def calc_abg_dims(self, abg_biomass, cm=True):
return (basal_dia, shoot_sys_width, height)

def _calc_shoot_width_from_basal_dia(self, basal_dia, cm=False):
# Apply log-log linear relationship to estimate canopy width from basal d
canopy_area = np.zeros_like(basal_dia)
filter = np.nonzero(basal_dia > self.morph_params["basal_dia"]["min"])
if cm is True:
Expand All @@ -127,6 +130,7 @@ def _calc_shoot_width_from_basal_dia(self, basal_dia, cm=False):
return shoot_width

def _calc_height_from_basal_dia(self, basal_dia, cm=False):
# Apply log-log linear relationship to estimate height from basal d
height = np.zeros_like(basal_dia)
filter = np.nonzero(basal_dia > self.morph_params["basal_dia"]["min"])
if cm is True:
Expand All @@ -137,6 +141,7 @@ def _calc_height_from_basal_dia(self, basal_dia, cm=False):
return height

def _calc_basal_dia_from_shoot_width(self, shoot_width, cm=False):
# Apply log-log linear relationship to estimate basal d from canopy width
basal_dia = np.zeros_like(shoot_width)
filter = np.nonzero(shoot_width > self.morph_params["shoot_sys_width"]["min"])
canopy_area = 0.25 * np.pi * shoot_width**2
Expand All @@ -148,6 +153,8 @@ def _calc_basal_dia_from_shoot_width(self, shoot_width, cm=False):
return basal_dia


"""
Not implemented in this version but code preserved here for future use
class Multi_Dimensional(Biomass):
def __init__(self, params):
# Not implemented yet but saved here for future versions allowing
Expand Down Expand Up @@ -180,3 +187,4 @@ def _apply3_allometry_eq_for_xs(self, ys, zs, predict_var):
c = self.morph_params["empirical_coeffs"][predict_var]["c"]
ln_xs = (ln_zs - a - c * ln_ys) / b
return np.exp(ln_xs)
"""
104 changes: 84 additions & 20 deletions src/landlab/components/genveg/dispersal.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,61 +7,125 @@
class Repro:
def __init__(self, params):
grow_params = params["grow_params"]
disperse_params = params["disperse_params"]
self.min_size = grow_params["growth_biomass"]["min"]
self.min_size_for_repro = disperse_params["min_size_dispersal"]


class Clonal(Repro):
def __init__(self, params):
disperse_params = params["dispersal_params"]
disperse_params = params["disperse_params"]
super().__init__(params)
self.unit_cost = disperse_params["unit_cost_dispersal"]
self.max_dist_dispersal = disperse_params["max_dist_dispersal"]
self.max_dist_dispersal = disperse_params["max_dist_runner"]

def disperse(self, plants):
"""
This method determines the potential location and cost
of clonal dispersal. A separate method in integrator.py
determines if dispersal is successful based if the potential location
is occupied.
of clonal dispersal. A separate method in
integrator.py determines if dispersal is successful if the
potential location is unoccupied.
"""
max_runner_length = np.zeros_like(plants["root"])
available_carb = plants["reproductive"] - 2 * self.min_size
max_runner_length[available_carb > 0] = (
available_carb[available_carb > 0] / self.unit_cost
)
runner_length = rng.uniform(
low=0.05,
low=0.02,
high=self.max_dist_dispersal,
size=plants.size,
)
pup_azimuth = np.deg2rad(rng.uniform(low=0.01, high=360, size=plants.size))

filter = np.nonzero(runner_length <= max_runner_length)

plants["pup_x_loc"][filter] = (
# Save to first position of pup subarray
plants["dispersal"]["pup_x_loc"][filter] = (
runner_length[filter] * np.cos(pup_azimuth[filter])
+ plants["x_loc"][filter]
)
plants["pup_y_loc"][filter] = (
plants["dispersal"]["pup_y_loc"][filter] = (
runner_length[filter] * np.sin(pup_azimuth)[filter]
+ plants["y_loc"][filter]
)
plants["pup_cost"][filter] = runner_length[filter] * self.unit_cost

plants["dispersal"]["pup_cost"][filter] = runner_length[filter] * self.unit_cost
return plants


class Seed(Repro):
def __init__(self, params):
pass
def __init__(self, params, dt):
super().__init__(params)
params = params["disperse_params"]
self.biomass_to_seedlings = params["mass_to_seedling_rate"]
self.mean_dispersal_distance = params["mean_seed_distance"]
self.max_dispersal_distance = params["max_seed_distance"]
self.dispersal_shape = params["seed_distribution_shape_param"]
self.seed_size = params["seed_mass"]
self.seed_efficiency = params["seed_efficiency"]
self.dt = dt

def disperse(self):
print("New plant emerges from seed some distance within parent plant")
def disperse(self, plants, total_biomass):
"""
This method implements dispersal using an algorithm similar to Vincenot 2016.
The log-normal distribution shape can be altered to accomodate many patterns of seed
distribution. Seed production is dependent on plant size and is limited by
biomass in the reproductive organ system.
"""
# Tracking max seeds that can be produced based on reproductive mass available
max_seeds = np.floor(plants["reproductive"] / (self.seed_size * self.seed_efficiency))
# Partial seedlings are banked to accomodate species with low seedling production rates
reserve_part, num_seedlings = np.modf(
(total_biomass * self.biomass_to_seedlings * self.dt.astype(int)),
out=(np.zeros_like(plants["reproductive"]), np.zeros_like(plants["reproductive"])),
where=(total_biomass >= self.min_size_for_repro)
)
num_seedlings[num_seedlings > max_seeds] = max_seeds
plants["reproductive"] -= num_seedlings * self.seed_size / self.seed_efficiency
plants["dispersal"]["seedling_reserve"] += reserve_part
if plants["dispersal"]["seedling_reserve"] >= 1:
num_seedlings += np.floor(plants["dispersal"]["seedling_reserve"])
plants["dispersal"]["seedling_reserve"] -= np.floor(plants["dispersal"]["seedling_reserve"])
# Loop through each plant to assign seedling locations
for i in range(plants.size):
count = num_seedlings[i].astype(int)
if count == 0:
break
dist_from_plant = rng.lognormal(self.mean_dispersal_distance, self.dispersal_shape, size=count)
dist_from_plant[dist_from_plant > self.max_dispersal_distance] = 0.0
pup_azimuth = np.deg2rad(rng.uniform(low=0.01, high=360, size=count))
plants["dispersal"]["seedling_x_loc"][i][:count] = (
dist_from_plant * np.cos(pup_azimuth) + plants["x_loc"][i]
)
plants["dispersal"]["seedling_y_loc"][i][:count] = (
dist_from_plant * np.sin(pup_azimuth) + plants["y_loc"][i]
)
return plants


class Random(Repro):
def __init__(self, params):
pass
def __init__(self, params, cell_area, extent, reference):
super().__init__(params)
self.daily_col_prob = params["disperse_params"]["daily_colonization_probability"]
self.cell_area = cell_area
# Note reference is longitude-latitude and extent is y,x
self.grid_boundary = (
reference[1],
reference[1] + extent[1],
reference[0],
reference[0] + extent[0]
)

def disperse(self):
print("New plant randomly appears")
def disperse(self, plants):
filter = (rng.uniform(0, 1, size=plants["root"].size) > (self.daily_col_prob * self.cell_area))
plants["dispersal"]["rand_x_loc"][:, 0] = rng.uniform(
low=self.grid_boundary[0],
high=self.grid_boundary[1],
size=plants["root"].size
)
plants["dispersal"]["rand_y_loc"][:, 0] = rng.uniform(
low=self.grid_boundary[2],
high=self.grid_boundary[3],
size=plants["root"].size
)
plants["dispersal"]["rand_x_loc"][filter] = np.nan
plants["dispersal"]["rand_y_loc"][filter] = np.nan
return plants
7 changes: 4 additions & 3 deletions src/landlab/components/genveg/duration.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,10 @@ def __init__(self, species_grow_params, duration_params, dt, green_parts):
)

def emerge(self, plants, available_mass, persistent_total_mass):
print("I emerge from dormancy and I am a deciduous perennial")
# next steps are to clean this up using same approach as dormancy

"""
This method allows deciduous perennial plants to emerge from dormancy
using non-structural biomass stored in the persistent parts of the plant.
"""
total_mass_new_green = np.zeros_like(plants["root_biomass"])
new_green_biomass = {}
for part in self.green_parts:
Expand Down
134 changes: 0 additions & 134 deletions src/landlab/components/genveg/form.py

This file was deleted.

Loading
Loading