• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

csdms / bmi-example-python / 16892127809

11 Aug 2025 09:05PM UTC coverage: 92.784%. Remained the same
16892127809

Pull #38

github

pre-commit-ci[bot]
[pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
Pull Request #38: [pre-commit.ci] pre-commit autoupdate

180 of 194 relevant lines covered (92.78%)

0.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/heat/heat.py
1
"""The 2D heat model."""
2

3
from __future__ import annotations
1✔
4

5
from io import TextIOBase
1✔
6

7
import numpy as np
1✔
8
import yaml
1✔
9
from numpy.typing import NDArray
1✔
10
from scipy import ndimage
1✔
11

12

13
def solve_2d(
1✔
14
    temp: NDArray[np.float64],
15
    spacing: tuple[float, ...],
16
    out: NDArray[np.float64] | None = None,
17
    alpha: float = 1.0,
18
    time_step: float = 1.0,
19
) -> NDArray[np.float64]:
20
    """Solve the 2D Heat Equation on a uniform mesh.
21

22
    Parameters
23
    ----------
24
    temp : ndarray
25
        Temperature.
26
    spacing : array_like
27
        Grid spacing in the row and column directions.
28
    out : ndarray (optional)
29
        Output array.
30
    alpha : float (optional)
31
        Thermal diffusivity.
32
    time_step : float (optional)
33
        Time step.
34

35
    Returns
36
    -------
37
    result : ndarray
38
        The temperatures after time *time_step*.
39

40
    Examples
41
    --------
42
    >>> from heat import solve_2d
43
    >>> z0 = np.zeros((3, 3))
44
    >>> z0[1:-1, 1:-1] = 1.
45
    >>> solve_2d(z0, (1., 1.), alpha=.25)
46
    array([[0. , 0. , 0. ],
47
           [0. , 0.5, 0. ],
48
           [0. , 0. , 0. ]])
49
    """
50
    dy2, dx2 = spacing[0] ** 2, spacing[1] ** 2
1✔
51
    stencil = (
1✔
52
        np.array([[0.0, dy2, 0.0], [dx2, -2.0 * (dx2 + dy2), dx2], [0.0, dy2, 0.0]])
53
        * alpha
54
        * time_step
55
        / (2.0 * (dx2 * dy2))
56
    )
57

58
    if out is None:
1✔
59
        out = np.empty_like(temp)
1✔
60

61
    ndimage.convolve(temp, stencil, output=out)
1✔
62
    out[(0, -1), :] = 0.0
1✔
63
    out[:, (0, -1)] = 0.0
1✔
64
    return np.add(temp, out, out=out)
1✔
65

66

67
class Heat:
1✔
68
    """Solve the Heat equation on a grid.
69

70
    Examples
71
    --------
72
    >>> heat = Heat()
73
    >>> heat.time
74
    0.0
75
    >>> heat.time_step
76
    0.25
77
    >>> heat.advance_in_time()
78
    >>> heat.time
79
    0.25
80

81
    >>> heat = Heat(shape=(5, 5))
82
    >>> heat.temperature = np.zeros_like(heat.temperature)
83
    >>> heat.temperature[2, 2] = 1.
84
    >>> heat.advance_in_time()
85

86
    >>> heat = Heat(alpha=.5)
87
    >>> heat.time_step
88
    0.5
89
    >>> heat = Heat(alpha=.5, spacing=(2., 3.))
90
    >>> heat.time_step
91
    2.0
92
    """
93

94
    def __init__(
1✔
95
        self,
96
        shape: tuple[int, int] = (10, 20),
97
        spacing: tuple[float, float] = (1.0, 1.0),
98
        origin: tuple[float, float] = (0.0, 0.0),
99
        alpha: float = 1.0,
100
    ) -> None:
101
        """Create a new heat model.
102

103
        Parameters
104
        ---------
105
        shape : array_like, optional
106
            The shape of the solution grid as (*rows*, *columns*).
107
        spacing : array_like, optional
108
            Spacing of grid rows and columns.
109
        origin : array_like, optional
110
            Coordinates of lower left corner of grid.
111
        alpha : float
112
            Alpha parameter in the heat equation.
113
        """
114
        self._shape = shape
1✔
115
        self._spacing = spacing
1✔
116
        self._origin = origin
1✔
117
        self._time = 0.0
1✔
118
        self._alpha = alpha
1✔
119
        self._time_step = min(spacing) ** 2 / (4.0 * self._alpha)
1✔
120

121
        self._temperature = np.random.random(self._shape)
1✔
122
        self._next_temperature = np.empty_like(self._temperature)
1✔
123

124
    @property
1✔
125
    def time(self) -> float:
1✔
126
        """Current model time."""
127
        return self._time
1✔
128

129
    @property
1✔
130
    def temperature(self) -> NDArray[np.float64]:
1✔
131
        """Temperature of the plate."""
132
        return self._temperature
1✔
133

134
    @temperature.setter
1✔
135
    def temperature(self, new_temp: float) -> None:
1✔
136
        """Set the temperature of the plate.
137

138
        Parameters
139
        ----------
140
        new_temp : array_like
141
            The new temperatures.
142
        """
143
        self._temperature[:] = new_temp
1✔
144

145
    @property
1✔
146
    def time_step(self) -> float:
1✔
147
        """Model time step."""
148
        return self._time_step
1✔
149

150
    @time_step.setter
1✔
151
    def time_step(self, time_step: float) -> None:
1✔
152
        """Set model time step."""
153
        self._time_step = time_step
1✔
154

155
    @property
1✔
156
    def shape(self) -> tuple[int, int]:
1✔
157
        """Shape of the model grid."""
158
        return self._shape
1✔
159

160
    @property
1✔
161
    def spacing(self) -> tuple[float, float]:
1✔
162
        """Spacing between nodes of the model grid."""
163
        return self._spacing
1✔
164

165
    @property
1✔
166
    def origin(self) -> tuple[float, float]:
1✔
167
        """Origin coordinates of the model grid."""
168
        return self._origin
1✔
169

170
    @classmethod
1✔
171
    def from_file_like(cls: type[Heat], file_like: TextIOBase) -> Heat:
1✔
172
        """Create a Heat object from a file-like object.
173

174
        Parameters
175
        ----------
176
        file_like : file_like
177
            Input parameter file.
178

179
        Returns
180
        -------
181
        Heat
182
            A new instance of a Heat object.
183
        """
184
        config = yaml.safe_load(file_like)
1✔
185
        return cls(**config)
1✔
186

187
    def advance_in_time(self) -> None:
1✔
188
        """Calculate new temperatures for the next time step."""
189
        solve_2d(
1✔
190
            self._temperature,
191
            self._spacing,
192
            out=self._next_temperature,
193
            alpha=self._alpha,
194
            time_step=self._time_step,
195
        )
196
        np.copyto(self._temperature, self._next_temperature)
1✔
197

198
        self._time += self._time_step
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc