Specimen - Snailz Docs

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
    @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
        """

        table_name: ClassVar[str] = "specimen"
        _next_id: 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[Self]:
            """
            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)

### `__post_init__()`

Validate fields and generate unique identifier.

Raises:

Type | Description
--- | ---
`ValueError` | If validation fails.

Source code in `src/snailz/specimen.py`

    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    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[Self]` | List of specimens.

Source code in `src/snailz/specimen.py`

     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
    @classmethod
    def make(cls, params: Parameters, grids: list[Grid], species: Species) -> list[Self]:
        """
        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`

    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    @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`

    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    @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)