dtc_super_resolution 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. dtc_super_resolution-0.1.0/LICENSE +14 -0
  2. dtc_super_resolution-0.1.0/PKG-INFO +61 -0
  3. dtc_super_resolution-0.1.0/README.md +28 -0
  4. dtc_super_resolution-0.1.0/pyproject.toml +129 -0
  5. dtc_super_resolution-0.1.0/src/dtc_super_resolution/__init__.py +1 -0
  6. dtc_super_resolution-0.1.0/src/dtc_super_resolution/monitoring.py +26 -0
  7. dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/__init__.py +1 -0
  8. dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/ml_module/__init__.py +1 -0
  9. dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/ml_module/attention_unet/__init__.py +1 -0
  10. dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/ml_module/attention_unet/rcm_emulator_parts.py +372 -0
  11. dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/ml_module/attention_unet/rcm_emulator_unet.py +104 -0
  12. dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/ml_module/basic_unet/__init__.py +1 -0
  13. dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/ml_module/basic_unet/unet.py +88 -0
  14. dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/ml_module/basic_unet/unet_parts.py +179 -0
  15. dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/ml_module/datasets.py +83 -0
  16. dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/ml_module/inference.py +49 -0
  17. dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/ml_module/inference_helpers.py +301 -0
  18. dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/ml_module/models_config.yaml +15 -0
  19. dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/pre_processing_module/__init__.py +1 -0
  20. dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/pre_processing_module/preprocessing.py +249 -0
  21. dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/pre_processing_module/reprojection.py +167 -0
  22. dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator/pre_processing_module/tiling.py +589 -0
  23. dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator_entrypoint.py +51 -0
  24. dtc_super_resolution-0.1.0/src/dtc_super_resolution/rcm_emulator_loc_entrypoint.py +56 -0
@@ -0,0 +1,14 @@
1
+ Copyright 2026 DTC-Ice-Sheets (DTC-IS) consortium
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
4
+ documentation files (the “Software”), to deal in the Software without restriction, including without limitation the
5
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
6
+ persons to whom the Software is furnished to do so, subject to the following conditions:
7
+
8
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
9
+ Software.
10
+
11
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
12
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
13
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
14
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,61 @@
1
+ Metadata-Version: 2.4
2
+ Name: dtc_super_resolution
3
+ Version: 0.1.0
4
+ Summary: A DTC-IS module that leverages super-resolution modeling techniques to enhance the monitoring and prediction of surface climate conditions over Greenland and Antarctica.
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Author: Stef Lhermitte
8
+ Author-email: stef.lhermitte@kuleuven.be
9
+ Requires-Python: >=3.12,<3.15
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Requires-Dist: cartopy (>=0.25.0,<0.26.0)
16
+ Requires-Dist: cyclopts (>=4.5.1,<5.0.0)
17
+ Requires-Dist: dotenv (>=0.9.9,<0.10.0)
18
+ Requires-Dist: fsspec[http] (>=2026.9.0,<2027.0.0)
19
+ Requires-Dist: ipykernel (>=7.3.0,<8.0.0)
20
+ Requires-Dist: jupyter (>=1.1.1,<2.0.0)
21
+ Requires-Dist: matplotlib (>=3.11.0,<4.0.0)
22
+ Requires-Dist: numpy (>=2.4,<3.0)
23
+ Requires-Dist: pydantic (>=2.12.5,<3.0.0)
24
+ Requires-Dist: rioxarray (==0.19)
25
+ Requires-Dist: scipy (>=1.18.0,<2.0.0)
26
+ Requires-Dist: seaborn (>=0.13.2,<0.14.0)
27
+ Requires-Dist: torch (==2.10.0)
28
+ Requires-Dist: tqdm (>=4.68.4,<5.0.0)
29
+ Requires-Dist: zarr (>=3.2.1,<4.0.0)
30
+ Project-URL: Homepage, https://github.com/dtc-ice-sheets/dtc_super_resolution
31
+ Description-Content-Type: text/markdown
32
+
33
+ ![Tests](https://github.com/dtc-ice-sheets/dtc_super_resolution/actions/workflows/dtc_super_resolution_test.yml/badge.svg)
34
+ ![Deployment](https://github.com/dtc-ice-sheets/dtc_super_resolution/actions/workflows/dtc_super_resolution_deploy.yml/badge.svg)
35
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/DTC-Ice-Sheets/dtc_super_resolution/blob/main/LICENSE)
36
+
37
+ # DTC-IS Super Resolution Module
38
+
39
+ The `dtc_super_resolution` module is a sub-component of the
40
+ [Digital Twin Component for Ice Sheets (DTC-IS)](https://dtc-ice-sheets.org/), an ESA-funded, EO-driven digital twin project.
41
+
42
+ This software package leverages super-resolution modeling techniques to enhance the monitoring and prediction of surface climate conditions over Greenland and Antarctica.
43
+
44
+ For an in-depth overview of the dtc_super_resolution module please [read the docs](https://dtc-ice-sheets.github.io/dtc_super_resolution/).
45
+
46
+ The documentation includes sections on:
47
+
48
+ 1. [Getting started](https://dtc-ice-sheets.github.io/dtc_super_resolution/quick-start.html)
49
+ 1. [Scientific overview](https://dtc-ice-sheets.github.io/dtc_super_resolution/overview.html)
50
+ 1. [API reference](https://dtc-ice-sheets.github.io/dtc_super_resolution/api.html)
51
+
52
+ For a more interactive way of exploring the system, please visit the digital twin's [dashboard](https://dashboards.dtc-ice-sheets.org/).
53
+
54
+ ## Installation for Use
55
+
56
+ You can install this package with pip:
57
+
58
+ ``` bash
59
+ pip install dtc_super_resolution
60
+ ```
61
+
@@ -0,0 +1,28 @@
1
+ ![Tests](https://github.com/dtc-ice-sheets/dtc_super_resolution/actions/workflows/dtc_super_resolution_test.yml/badge.svg)
2
+ ![Deployment](https://github.com/dtc-ice-sheets/dtc_super_resolution/actions/workflows/dtc_super_resolution_deploy.yml/badge.svg)
3
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/DTC-Ice-Sheets/dtc_super_resolution/blob/main/LICENSE)
4
+
5
+ # DTC-IS Super Resolution Module
6
+
7
+ The `dtc_super_resolution` module is a sub-component of the
8
+ [Digital Twin Component for Ice Sheets (DTC-IS)](https://dtc-ice-sheets.org/), an ESA-funded, EO-driven digital twin project.
9
+
10
+ This software package leverages super-resolution modeling techniques to enhance the monitoring and prediction of surface climate conditions over Greenland and Antarctica.
11
+
12
+ For an in-depth overview of the dtc_super_resolution module please [read the docs](https://dtc-ice-sheets.github.io/dtc_super_resolution/).
13
+
14
+ The documentation includes sections on:
15
+
16
+ 1. [Getting started](https://dtc-ice-sheets.github.io/dtc_super_resolution/quick-start.html)
17
+ 1. [Scientific overview](https://dtc-ice-sheets.github.io/dtc_super_resolution/overview.html)
18
+ 1. [API reference](https://dtc-ice-sheets.github.io/dtc_super_resolution/api.html)
19
+
20
+ For a more interactive way of exploring the system, please visit the digital twin's [dashboard](https://dashboards.dtc-ice-sheets.org/).
21
+
22
+ ## Installation for Use
23
+
24
+ You can install this package with pip:
25
+
26
+ ``` bash
27
+ pip install dtc_super_resolution
28
+ ```
@@ -0,0 +1,129 @@
1
+ [build-system]
2
+ requires = ["poetry>=1.5"]
3
+ build-backend = "poetry.core.masonry.api"
4
+
5
+ [tool.poetry]
6
+ name = "dtc_super_resolution"
7
+ version = "0.1.0"
8
+ description = "A DTC-IS module that leverages super-resolution modeling techniques to enhance the monitoring and prediction of surface climate conditions over Greenland and Antarctica."
9
+ authors = [
10
+ "Stef Lhermitte <stef.lhermitte@kuleuven.be>",
11
+ "Earthwave Ltd <info@earthwave.co.uk>",
12
+ ]
13
+ license = "MIT"
14
+ readme = "README.md"
15
+ homepage = "https://github.com/dtc-ice-sheets/dtc_super_resolution"
16
+ packages = [{ include = "dtc_super_resolution", from = "src" }]
17
+
18
+ [[tool.poetry.source]]
19
+ name = "pytorch-cpu"
20
+ url = "https://download.pytorch.org/whl/cpu"
21
+ priority = "explicit"
22
+
23
+ [tool.poetry.dependencies]
24
+ python = ">=3.12,<3.15"
25
+ numpy = "^2.4"
26
+ cyclopts = "^4.5.1"
27
+ pydantic = "^2.12.5"
28
+ cartopy = "^0.25.0"
29
+ dotenv = "^0.9.9"
30
+ ipykernel = "^7.3.0"
31
+ jupyter = "^1.1.1"
32
+ matplotlib = "^3.11.0"
33
+ rioxarray = "0.19"
34
+ seaborn = "^0.13.2"
35
+ torch = { version = "2.10.0", source = "pytorch-cpu" }
36
+ tqdm = "^4.68.4"
37
+ zarr = "^3.2.1"
38
+ scipy = "^1.18.0"
39
+ fsspec = { extras = ["http"], version = "^2026.9.0" }
40
+
41
+ [tool.poetry.group.dev.dependencies]
42
+ pytest = "^9.0.2"
43
+ typeguard = "^4.4.4"
44
+ pre-commit = "^4.5"
45
+ ruff = "^0.14.14"
46
+ bandit = "^1.9.3"
47
+ pytest-cov = "^7.0.0"
48
+ sphinx = "^9.1.0"
49
+ sphinx-rtd-theme = "^3.0.2"
50
+ sphinx-copybutton = "^0.5.2"
51
+ commitizen = "^4.11.6"
52
+ pydoclint = "^0.8.3"
53
+
54
+ [tool.poetry.group.deployment.dependencies]
55
+ dtc-wrapper = { git = "https://github.com/DTC-Ice-Sheets/dtc_wrapper.git", tag = "0.0.42" }
56
+
57
+ [tool.ruff]
58
+ line-length = 120
59
+ target-version = "py312"
60
+
61
+ [tool.ruff.lint]
62
+ preview = true
63
+ select = [
64
+ "T20", # Disallow print statements
65
+ "A", # Ambiguity
66
+ "ANN", # Annotations
67
+ "B", # Bad practices
68
+ "C90", # Complexity
69
+ "D", # Documentation
70
+ "E", # Errors
71
+ "F", # Pyflakes (e.g. unused imports and vars)
72
+ "G", # logging formatting
73
+ "I", # Imports
74
+ "N", # Naming
75
+ "Q", # Quoting
76
+ "W", # Warnings
77
+ "PT", # pytest-style
78
+ "SIM", # flake8-simplify
79
+ "C4", # flake8-comprehensions
80
+ "ARG", # flake8-unused-arguments
81
+ "PIE", # flake8-pie
82
+ "RUF", # Ruff-specific rules
83
+ "BLE", # Blind except
84
+ "RET", # Return statements
85
+ "PTH", # Use pathlib
86
+ "PD", # Pandas
87
+ ]
88
+
89
+ [tool.pydoclint]
90
+ style = 'numpy'
91
+ check_style_mismatch = "true"
92
+
93
+ mccabe = { max-complexity = 10 }
94
+
95
+ ignore = [
96
+ "D203", # One blank line required before class docstring (conflicts with D211)
97
+ "D204", # One blank line required after class docstring
98
+ "D205", # Docstring with no content
99
+ "D212", # Multi-line docstring summary should start at the second line
100
+ "D213", # Multi-line docstring summary should start at the first line
101
+ ]
102
+
103
+ [tool.ruff.lint.per-file-ignores]
104
+ "tests/**" = [
105
+ "T201", # Allow print statements in test files, as they are commonly used for debugging
106
+ "E402", # Allow 'import' statements not being at the top of the file (common in test files for clarity)
107
+ "S101", # Allow the use of 'assert' statements in test files (common practice in pytest)
108
+ "D103", # Ignore missing docstring warnings for functions in test files (commonly not required for small test functions)
109
+ "D102", # Ignore missing docstring warnings for classes in test files
110
+ "D101", # Ignore missing docstring warnings in test files
111
+ "ANN", # Ignore missing type annotations for functions in test files (commonly not required for small test functions)
112
+ ]
113
+ "**.ipynb" = [
114
+ "T201", # Allow print statements in Jupyter notebooks, as they are commonly used for debugging and output display
115
+ "E402", # Allow 'import' statements not being at the top of the file (common in notebooks for clarity)
116
+ "S101", # Allow the use of 'assert' statements in notebooks (common practice in exploratory coding)
117
+ ]
118
+
119
+ [tool.ruff.lint.pydocstyle]
120
+ convention = "numpy" # Accepts: "google", "numpy", or "pep257"
121
+ [tool.bandit.assert_used]
122
+ skips = ['*_test.py', '*/test_*.py']
123
+
124
+ [tool.pytest.ini_options]
125
+ addopts = "--disable-warnings --cov dtc_super_resolution --cov-fail-under=80"
126
+ typeguard-packages = """dtc_super_resolution"""
127
+ typeguard-debug-instrumentation = true
128
+ typeguard-forward-ref-policy = "ERROR"
129
+ typeguard-collection-check-strategy = "ALL_ITEMS"
@@ -0,0 +1 @@
1
+ """Super resolution package."""
@@ -0,0 +1,26 @@
1
+ """Monitoring and logging tools."""
2
+
3
+ import logging
4
+
5
+
6
+ def setup_logging() -> None:
7
+ """
8
+ Set up root logger.
9
+
10
+ This configuration will be inherited by all module-level loggers created within the package.
11
+ """
12
+ # Create the root logger
13
+ logger = logging.getLogger()
14
+ logger.setLevel(logging.INFO)
15
+
16
+ # Create a console handler and set the level to info
17
+ console_handler = logging.StreamHandler()
18
+ console_handler.setLevel(logging.INFO)
19
+
20
+ # Create a formatter and set it for the console handler
21
+ formatter = logging.Formatter("[%(asctime)s] %(levelname)s in %(name)s: %(message)s")
22
+ console_handler.setFormatter(formatter)
23
+
24
+ # Add the console handler to the root logger (only if not already added)
25
+ if not logger.handlers:
26
+ logger.addHandler(console_handler)
@@ -0,0 +1 @@
1
+ """RCM emulator package."""
@@ -0,0 +1 @@
1
+ """Machine learning module."""
@@ -0,0 +1 @@
1
+ """Attention U-Net model for super-resolution of climate data."""
@@ -0,0 +1,372 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """Parts of the U-Net model."""
4
+
5
+ # Base model taken from: https://github.com/milesial/Pytorch-UNet
6
+ import torch
7
+ import torch.nn as nn
8
+ import torch.nn.functional as functional
9
+
10
+
11
+ class DoubleConvDS(nn.Module):
12
+ """(convolution => [BN] => ReLU) * 2."""
13
+
14
+ def __init__(self, in_channels: int, out_channels: int, mid_channels: int = 0, kernels_per_layer: int = 1) -> None:
15
+ super().__init__()
16
+ if not mid_channels:
17
+ mid_channels = out_channels
18
+ self.double_conv = nn.Sequential(
19
+ DepthwiseSeparableConv(
20
+ in_channels,
21
+ mid_channels,
22
+ kernel_size=3,
23
+ kernels_per_layer=kernels_per_layer,
24
+ padding=1,
25
+ ),
26
+ nn.GroupNorm(8, mid_channels),
27
+ nn.ReLU(inplace=True),
28
+ DepthwiseSeparableConv(
29
+ mid_channels,
30
+ out_channels,
31
+ kernel_size=3,
32
+ kernels_per_layer=kernels_per_layer,
33
+ padding=1,
34
+ ),
35
+ nn.GroupNorm(8, out_channels),
36
+ nn.ReLU(inplace=True),
37
+ )
38
+
39
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
40
+ """Forward pass for the DoubleDSConv module."""
41
+ return self.double_conv(x)
42
+
43
+
44
+ class UpLastConv(nn.Module):
45
+ """Upscaling then double conv without concatenating with skip connection."""
46
+
47
+ def __init__(
48
+ self,
49
+ in_channels: int,
50
+ out_channels: int,
51
+ mid_channels: int,
52
+ bilinear: bool = True,
53
+ kernels_per_layer: int = 1,
54
+ ) -> None:
55
+ super().__init__()
56
+
57
+ # if bilinear, use the normal convolutions to reduce the number of channels
58
+ if bilinear:
59
+ self.up = nn.Upsample(scale_factor=2, mode="bilinear", align_corners=True)
60
+ # self.conv = DoubleConv(in_channels, out_channels, in_channels // 2)
61
+ self.conv = DoubleConvDS(
62
+ in_channels,
63
+ out_channels,
64
+ in_channels // 2,
65
+ kernels_per_layer=kernels_per_layer,
66
+ )
67
+ else:
68
+ self.up = nn.ConvTranspose2d(in_channels, in_channels // 2, kernel_size=3, stride=2)
69
+ # self.conv = DoubleConv(mid_channels, out_channels)
70
+ self.conv = DoubleConvDS(mid_channels, out_channels, kernels_per_layer=kernels_per_layer)
71
+
72
+ def forward(self, x: torch.Tensor, zsize: int = 64) -> torch.Tensor:
73
+ """Forward pass for the UpLastConv module."""
74
+ x1 = self.up(x)
75
+ # input is CHW
76
+ diff_y = zsize - x1.size()[2]
77
+ diff_x = zsize - x1.size()[3]
78
+
79
+ x1 = functional.pad(x1, [diff_x // 2, diff_x - diff_x // 2, diff_y // 2, diff_y - diff_y // 2])
80
+ return self.conv(x1)
81
+
82
+
83
+ class DownDS(nn.Module):
84
+ """Downscaling with maxpool then double conv."""
85
+
86
+ def __init__(self, in_channels: int, out_channels: int, kernels_per_layer: int = 1) -> None:
87
+ super().__init__()
88
+ self.maxpool_conv = nn.Sequential(
89
+ nn.MaxPool2d(2),
90
+ DoubleConvDS(in_channels, out_channels, kernels_per_layer=kernels_per_layer),
91
+ )
92
+
93
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
94
+ """Forward pass for the DownDS module."""
95
+ return self.maxpool_conv(x)
96
+
97
+
98
+ class UpDS(nn.Module):
99
+ """Upscaling then double conv."""
100
+
101
+ def __init__(self, in_channels: int, out_channels: int, bilinear: bool = True, kernels_per_layer: int = 1) -> None:
102
+ super().__init__()
103
+
104
+ # if bilinear, use the normal convolutions to reduce the number of channels
105
+ if bilinear:
106
+ self.up = nn.Upsample(scale_factor=2, mode="bilinear", align_corners=True)
107
+ self.conv = DoubleConvDS(
108
+ in_channels,
109
+ out_channels,
110
+ in_channels // 2,
111
+ kernels_per_layer=kernels_per_layer,
112
+ )
113
+ else:
114
+ self.up = nn.ConvTranspose2d(in_channels, in_channels // 2, kernel_size=3, stride=2)
115
+ self.conv = DoubleConvDS(in_channels, out_channels, kernels_per_layer=kernels_per_layer)
116
+
117
+ def forward(self, x1: torch.Tensor, x2: torch.Tensor) -> torch.Tensor:
118
+ """Forward pass for the UpDS module."""
119
+ x1 = self.up(x1)
120
+ # input is CHW
121
+ diff_y = x2.size()[2] - x1.size()[2]
122
+ diff_x = x2.size()[3] - x1.size()[3]
123
+
124
+ x1 = functional.pad(x1, [diff_x // 2, diff_x - diff_x // 2, diff_y // 2, diff_y - diff_y // 2])
125
+ # if you have padding issues, see
126
+ # https://github.com/HaiyongJiang/U-Net-Pytorch-Unstructured-Buggy/commit/0e854509c2cea854e247a9c615f175f76fbb2e3a
127
+ # https://github.com/xiaopeng-liao/Pytorch-UNet/commit/8ebac70e633bac59fc22bb5195e513d5832fb3bd
128
+ x = torch.cat([x2, x1], dim=1)
129
+ return self.conv(x)
130
+
131
+
132
+ class OutConv(nn.Module):
133
+ """Output convolution."""
134
+
135
+ def __init__(self, in_channels: int, out_channels: int) -> None:
136
+ super().__init__()
137
+ self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=1)
138
+
139
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
140
+ """Forward pass for the OutConv module."""
141
+ return self.conv(x)
142
+
143
+
144
+ class InitialConv(nn.Module):
145
+ """Initial convolution to adapt spatial dimensions."""
146
+
147
+ def __init__(self, in_channels: int, out_channels: int, size_in: int, size_out: int) -> None:
148
+ super().__init__()
149
+ self.n_channels_in = in_channels
150
+ self.n_channels_out = out_channels
151
+ if size_in >= size_out:
152
+ # downsampling via valid convolution
153
+ diff = size_in - size_out + 1
154
+ self.adapt = nn.Sequential(
155
+ nn.Conv2d(in_channels, out_channels, kernel_size=diff),
156
+ nn.BatchNorm2d(out_channels),
157
+ nn.ReLU(inplace=True),
158
+ )
159
+ else:
160
+ # upsampling via bilinear resize + 3x3 conv
161
+ self.adapt = nn.Sequential(
162
+ nn.Upsample(size=size_out, mode="bilinear", align_corners=True),
163
+ nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1),
164
+ nn.BatchNorm2d(out_channels),
165
+ nn.ReLU(inplace=True),
166
+ )
167
+
168
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
169
+ """Forward pass for the InitialConv module."""
170
+ return self.adapt(x)
171
+
172
+
173
+ # class InitialConv(nn.Module):
174
+ # def __init__(self, in_channels, out_channels, sizex, size):
175
+ # super().__init__()
176
+ # self.diff = sizex - size + 1
177
+ # self.n_channels_in = in_channels
178
+ # self.n_channels_out = out_channels
179
+ # self.firstconv = nn.Sequential(
180
+ # nn.Conv2d(self.n_channels_in, self.n_channels_out, kernel_size=self.diff),
181
+ # nn.BatchNorm2d(out_channels),
182
+ # nn.ReLU(inplace=True),
183
+ # )
184
+
185
+ # def forward(self, x):
186
+ # x1 = self.firstconv(x)
187
+ # return x1
188
+
189
+
190
+ # Taken from https://discuss.pytorch.org/t/is-there-any-layer-like-tensorflows-space-to-depth-function/3487/14
191
+ class DepthToSpace(nn.Module):
192
+ """Depth to space operation."""
193
+
194
+ def __init__(self, block_size: int) -> None:
195
+ super().__init__()
196
+ self.bs = block_size
197
+
198
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
199
+ """Forward pass for the DepthToSpace module."""
200
+ n, c, h, w = x.size()
201
+ x = x.view(n, self.bs, self.bs, c // (self.bs**2), h, w) # (n, bs, bs, c//bs^2, h, w)
202
+ x = x.permute(0, 3, 4, 1, 5, 2).contiguous() # (n, c//bs^2, h, bs, w, bs)
203
+ return x.view(n, c // (self.bs**2), h * self.bs, w * self.bs) # (n, c//bs^2, h * bs, w * bs)
204
+
205
+
206
+ class SpaceToDepth(nn.Module):
207
+ """Space to depth operation."""
208
+
209
+ # Expects the following shape: Batch, Channel, Height, Width
210
+ def __init__(self, block_size: int) -> None:
211
+ super().__init__()
212
+ self.bs = block_size
213
+
214
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
215
+ """Forward pass for the SpaceToDepth module."""
216
+ n, c, h, w = x.size()
217
+ x = x.view(n, c, h // self.bs, self.bs, w // self.bs, self.bs) # (n, c, h//bs, bs, w//bs, bs)
218
+ x = x.permute(0, 3, 5, 1, 2, 4).contiguous() # (n, bs, bs, c, h//bs, w//bs)
219
+ return x.view(n, c * (self.bs**2), h // self.bs, w // self.bs) # (n, c*bs^2, h//bs, w//bs)
220
+
221
+
222
+ class DepthwiseSeparableConv(nn.Module):
223
+ """Depthwise separable convolution."""
224
+
225
+ def __init__(
226
+ self, in_channels: int, output_channels: int, kernel_size: int, padding: int = 0, kernels_per_layer: int = 1
227
+ ) -> None:
228
+ super().__init__()
229
+ # In Tensorflow DepthwiseConv2D has depth_multiplier instead of kernels_per_layer
230
+ self.depthwise = nn.Conv2d(
231
+ in_channels,
232
+ in_channels * kernels_per_layer,
233
+ kernel_size=kernel_size,
234
+ padding=padding,
235
+ groups=in_channels,
236
+ )
237
+ self.pointwise = nn.Conv2d(in_channels * kernels_per_layer, output_channels, kernel_size=1)
238
+
239
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
240
+ """Forward pass for the DepthwiseSeparableConv module."""
241
+ x = self.depthwise(x)
242
+ return self.pointwise(x)
243
+
244
+
245
+ class DoubleDense(nn.Module):
246
+ """(dense => [ReLU]) * 2."""
247
+
248
+ def __init__(self, in_channels: int, hidden_neurons: int, output_channels: int) -> None:
249
+ super().__init__()
250
+ self.dense1 = nn.Linear(in_channels, out_features=hidden_neurons)
251
+ self.dense2 = nn.Linear(in_features=hidden_neurons, out_features=hidden_neurons // 2)
252
+ self.dense3 = nn.Linear(in_features=hidden_neurons // 2, out_features=output_channels)
253
+
254
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
255
+ """Forward pass for the DoubleDense module."""
256
+ out = functional.relu(self.dense1(x.view(x.size(0), -1)))
257
+ out = functional.relu(self.dense2(out))
258
+ return self.dense3(out)
259
+
260
+
261
+ class DoubleDSConv(nn.Module):
262
+ """(convolution => [BN] => ReLU) * 2."""
263
+
264
+ def __init__(self, in_channels: int, out_channels: int) -> None:
265
+ super().__init__()
266
+ self.double_ds_conv = nn.Sequential(
267
+ DepthwiseSeparableConv(in_channels, out_channels, kernel_size=3, padding=1),
268
+ nn.BatchNorm2d(out_channels),
269
+ nn.ReLU(inplace=True),
270
+ DepthwiseSeparableConv(out_channels, out_channels, kernel_size=3, padding=1),
271
+ nn.BatchNorm2d(out_channels),
272
+ nn.ReLU(inplace=True),
273
+ )
274
+
275
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
276
+ """Forward pass for the DoubleDSConv module."""
277
+ return self.double_ds_conv(x)
278
+
279
+
280
+ class Flatten(nn.Module):
281
+ """Flatten module to reshape the input tensor."""
282
+
283
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
284
+ """Forward pass for the Flatten module."""
285
+ return x.view(x.size(0), -1)
286
+
287
+
288
+ class ChannelAttention(nn.Module):
289
+ """Channel attention module."""
290
+
291
+ def __init__(self, input_channels: int, reduction_ratio: int = 16) -> None:
292
+ super().__init__()
293
+ self.input_channels = input_channels
294
+ self.avg_pool = nn.AdaptiveAvgPool2d(1)
295
+ self.max_pool = nn.AdaptiveMaxPool2d(1)
296
+ # https://github.com/luuuyi/CBAM.PyTorch/blob/master/model/resnet_cbam.py
297
+ # uses Convolutions instead of Linear
298
+ self.MLP = nn.Sequential(
299
+ Flatten(),
300
+ nn.Linear(input_channels, input_channels // reduction_ratio),
301
+ nn.ReLU(),
302
+ nn.Linear(input_channels // reduction_ratio, input_channels),
303
+ )
304
+
305
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
306
+ """Forward pass for the ChannelAttention module."""
307
+ # Take the input and apply average and max pooling
308
+ avg_values = self.avg_pool(x)
309
+ max_values = self.max_pool(x)
310
+ out = self.MLP(avg_values) + self.MLP(max_values)
311
+ return x * torch.sigmoid(out).unsqueeze(2).unsqueeze(3).expand_as(x)
312
+
313
+
314
+ class SpatialAttention(nn.Module):
315
+ """Spatial attention module."""
316
+
317
+ def __init__(self, kernel_size: int = 7) -> None:
318
+ super().__init__()
319
+ assert kernel_size in (3, 7), "kernel size must be 3 or 7"
320
+ padding = 3 if kernel_size == 7 else 1
321
+ self.conv = nn.Conv2d(2, 1, kernel_size=kernel_size, padding=padding, bias=False)
322
+ self.bn = nn.Identity()
323
+
324
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
325
+ """Forward pass for the SpatialAttention module."""
326
+ avg_out = torch.mean(x, dim=1, keepdim=True)
327
+ max_out, _ = torch.max(x, dim=1, keepdim=True)
328
+ out = torch.cat([avg_out, max_out], dim=1)
329
+ out = self.conv(out)
330
+ out = self.bn(out)
331
+ return x * torch.sigmoid(out)
332
+
333
+
334
+ class CBAM(nn.Module):
335
+ """Convolutional Block Attention Module (CBAM)."""
336
+
337
+ def __init__(self, input_channels: int, reduction_ratio: int = 16, kernel_size: int = 7) -> None:
338
+ super().__init__()
339
+ self.channel_att = ChannelAttention(input_channels, reduction_ratio=reduction_ratio)
340
+ self.spatial_att = SpatialAttention(kernel_size=kernel_size)
341
+
342
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
343
+ """Forward pass for the CBAM module."""
344
+ out = self.channel_att(x)
345
+ return self.spatial_att(out)
346
+
347
+
348
+ class LinearUpsamplingZ(nn.Module):
349
+ """Linear upsampling of z to match the number of channels of downsampled x."""
350
+
351
+ # Linear upsampling of z to same number of channels as downsampled X
352
+ # so that can concatenate
353
+ def __init__(self, in_channels: int = 16, out_channels: int = 0) -> None:
354
+ super().__init__()
355
+
356
+ if out_channels == 0:
357
+ out_channels = 1024
358
+
359
+ self.linearUp = nn.Sequential(
360
+ nn.Linear(in_channels, 128),
361
+ nn.Linear(128, 256),
362
+ nn.Linear(256, 512),
363
+ nn.Linear(512, out_channels),
364
+ )
365
+
366
+ def forward(self, z: torch.Tensor) -> torch.Tensor:
367
+ """Forward pass for linear upsampling of z."""
368
+ # transform to remove last dimensions
369
+ z1 = z.view(z.shape[0], -1) # (batch, 16)
370
+ z5 = self.linearUp(z1.float()) # (batch, 1024)
371
+ # transform back to original shape
372
+ return z5.view(z5.shape[0], 1, 1, -1).transpose(3, 1) # (batch, 1024, 1, 1)