Skip to content

Specimen

Sampled specimens.

Specimen dataclass

Bases: BaseMixin

A single specimen.

Attributes:

Name Type Description
ident str

unique identifier

lat float

latitude where specimen collected (from grid cell)

lon float

longitude where specimen collected (from grid cell)

genome str

specimen genome

mass float

specimen mass (g)

diameter float

specimen diameter (mm)

collected date

date specimen was collected

Source code in src/snailz/specimen.py
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
@dataclass
class Specimen(BaseMixin):
    """
    A single specimen.

    Attributes:
        ident: unique identifier
        lat: latitude where specimen collected (from grid cell)
        lon: longitude where specimen collected (from grid cell)
        genome: specimen genome
        mass: specimen mass (g)
        diameter: specimen diameter (mm)
        collected: date specimen was collected
    """

    _next_id: ClassVar[IdGeneratorType] = id_generator("S", 4)

    ident: str = ""
    lat: float = 0.0
    lon: float = 0.0
    genome: str = ""
    mass: float = 0.0
    diameter: float = 0.0
    collected: date = date.min

    def __post_init__(self):
        """
        Validate fields and generate unique identifier.

        Raises:
            ValueError: If validation fails.
        """

        validate(self.ident == "", "specimen ID cannot be set externally")
        validate_lat_lon("specimen", self.lat, self.lon)
        validate(len(self.genome) > 0, "specimen must have genome")
        validate(self.mass > 0, "specimen must have positive mass")
        validate(self.diameter > 0, "specimen must have positive diameter")
        validate(
            self.collected > date.min, "specimen must have sensible collection date"
        )

        self.ident = next(self._next_id)
        self.mass = round(self.mass, SPECIMEN_PRECISION)
        self.diameter = round(self.diameter, SPECIMEN_PRECISION)

    @classmethod
    def make(
        cls, params: Parameters, grids: list[Grid], species: Species
    ) -> list["Specimen"]:
        """
        Construct multiple specimens.

        Args:
            params: Parameters object.
            grids: Grids that specimens are taken from.
            species: Species that specimens belong to.

        Returns:
            List of specimens.
        """

        result = []
        for _ in range(params.num_specimens):
            g = random.choice(grids)
            x = random.randint(0, g.size - 1)
            y = random.randint(0, g.size - 1)
            lat, lon = g.lat_lon(x, y)
            genome = species.random_genome(params)
            mass = cls.random_mass(params, g[x, y])
            diameter = cls.random_diameter(params, mass)
            collected = random_date(params.start_date, params.end_date)
            result.append(
                Specimen(
                    lat=lat,
                    lon=lon,
                    genome=genome,
                    mass=mass,
                    diameter=diameter,
                    collected=collected,
                )
            )

        return result

    @classmethod
    def random_diameter(cls, params: Parameters, mass: float) -> float:
        """
        Generate normal random diameter.

        Args:
            params: Parameters object.
            mass: pre-calculated mass.

        Returns:
            Random diameter for specimen.
        """

        return abs(random.gauss(mass * params.diam_ratio, params.diam_sigma))

    @classmethod
    def random_mass(cls, params: Parameters, pollution: float) -> float:
        """
        Generate log-normal mass distribution modified by pollution.

        Args:
            params: Parameters object.
            pollution: Pollution level in specimen's grid cell.

        Returns:
            Random mass for specimen.
        """

        mu = params.mass_beta_0 + params.mass_beta_1 * pollution
        log_mass = random.gauss(mu, params.mass_sigma)
        return math.exp(log_mass)

    @classmethod
    def table_name(cls) -> str:
        """Database table name."""

        return "specimen"

__post_init__()

Validate fields and generate unique identifier.

Raises:

Type Description
ValueError

If validation fails.

Source code in src/snailz/specimen.py
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
def __post_init__(self):
    """
    Validate fields and generate unique identifier.

    Raises:
        ValueError: If validation fails.
    """

    validate(self.ident == "", "specimen ID cannot be set externally")
    validate_lat_lon("specimen", self.lat, self.lon)
    validate(len(self.genome) > 0, "specimen must have genome")
    validate(self.mass > 0, "specimen must have positive mass")
    validate(self.diameter > 0, "specimen must have positive diameter")
    validate(
        self.collected > date.min, "specimen must have sensible collection date"
    )

    self.ident = next(self._next_id)
    self.mass = round(self.mass, SPECIMEN_PRECISION)
    self.diameter = round(self.diameter, SPECIMEN_PRECISION)

make(params, grids, species) classmethod

Construct multiple specimens.

Parameters:

Name Type Description Default
params Parameters

Parameters object.

required
grids list[Grid]

Grids that specimens are taken from.

required
species Species

Species that specimens belong to.

required

Returns:

Type Description
list[Specimen]

List of specimens.

Source code in src/snailz/specimen.py
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
@classmethod
def make(
    cls, params: Parameters, grids: list[Grid], species: Species
) -> list["Specimen"]:
    """
    Construct multiple specimens.

    Args:
        params: Parameters object.
        grids: Grids that specimens are taken from.
        species: Species that specimens belong to.

    Returns:
        List of specimens.
    """

    result = []
    for _ in range(params.num_specimens):
        g = random.choice(grids)
        x = random.randint(0, g.size - 1)
        y = random.randint(0, g.size - 1)
        lat, lon = g.lat_lon(x, y)
        genome = species.random_genome(params)
        mass = cls.random_mass(params, g[x, y])
        diameter = cls.random_diameter(params, mass)
        collected = random_date(params.start_date, params.end_date)
        result.append(
            Specimen(
                lat=lat,
                lon=lon,
                genome=genome,
                mass=mass,
                diameter=diameter,
                collected=collected,
            )
        )

    return result

random_diameter(params, mass) classmethod

Generate normal random diameter.

Parameters:

Name Type Description Default
params Parameters

Parameters object.

required
mass float

pre-calculated mass.

required

Returns:

Type Description
float

Random diameter for specimen.

Source code in src/snailz/specimen.py
111
112
113
114
115
116
117
118
119
120
121
122
123
124
@classmethod
def random_diameter(cls, params: Parameters, mass: float) -> float:
    """
    Generate normal random diameter.

    Args:
        params: Parameters object.
        mass: pre-calculated mass.

    Returns:
        Random diameter for specimen.
    """

    return abs(random.gauss(mass * params.diam_ratio, params.diam_sigma))

random_mass(params, pollution) classmethod

Generate log-normal mass distribution modified by pollution.

Parameters:

Name Type Description Default
params Parameters

Parameters object.

required
pollution float

Pollution level in specimen's grid cell.

required

Returns:

Type Description
float

Random mass for specimen.

Source code in src/snailz/specimen.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
@classmethod
def random_mass(cls, params: Parameters, pollution: float) -> float:
    """
    Generate log-normal mass distribution modified by pollution.

    Args:
        params: Parameters object.
        pollution: Pollution level in specimen's grid cell.

    Returns:
        Random mass for specimen.
    """

    mu = params.mass_beta_0 + params.mass_beta_1 * pollution
    log_mass = random.gauss(mu, params.mass_sigma)
    return math.exp(log_mass)

table_name() classmethod

Database table name.

Source code in src/snailz/specimen.py
143
144
145
146
147
@classmethod
def table_name(cls) -> str:
    """Database table name."""

    return "specimen"