44
44
#
45
45
46
46
'''
47
- ___
48
- ##**Notes**
47
+ This module defines the API to work with a trial space.
48
+ A trial space is foundational to reduced-order models.
49
+ In a ROM, a high-dimensional state is restricted to live within a low-dimensional trial space.
50
+ Mathematically, given a "FOM" vector $\\ mathbf{u} \\ in \\ mathbb{R}^{N_{\\ mathrm{vars}} N_{\\ mathrm{x}}}$, we can write
51
+ $$\\ mathbf{u} \\ approx \\ tilde{\\ mathbf{u}} \\ in \\ mathcal{V} + \\ mathbf{u}_{\\ mathrm{shift}}$$
52
+ where
53
+ - $\\ mathcal{V}$ with $\\ text{dim}(\\ mathcal{V}) = K \\ le N_{\\ mathrm{vars}} N_{\\ mathrm{x}}$ is the trial space
54
+ - $N_{\\ mathrm{vars}}$ is the number of PDE variables (e.g., 5 for the compressible Navier-Stokes equations in 3D)
55
+ - $N_{\\ mathrm{x}}$ is the number of spatial DOFs
56
+
57
+ Formally, we can describe this low-dimensional representation with a basis and an affine offset,
58
+ $$\\ tilde{\\ mathbf{u}} = \\ boldsymbol \\ Phi \\ hat{\\ mathbf{u}} + \\ mathbf{u}_{\\ mathrm{shift}}$$
59
+ where $\\ boldsymbol \\ Phi \\ in \\ mathbb{R}^{ N_{\\ mathrm{vars}} N_{\\ mathrm{x}} \\ times K}$ is the basis matrix
60
+ ($K$ is the number of basis), $\\ hat{\\ mathbf{u}} \\ in \\ mathbb{R}^{K}$ are the reduced, or generalized coordinates,
61
+ $\\ mathbf{u}_{\\ mathrm{shift}} \\ in \\ mathbb{R}^{ N_{\\ mathrm{vars}} N_{\\ mathrm{x}}}$ is the shift vector (or affine offset),
62
+ and, by definition, $\\ mathcal{V} \\ equiv \\ mathrm{range}(\\ boldsymbol \\ Phi)$.
63
+
64
+ The `TrialSpace` abstract class defined below encapsulates the information of an affine trial space, $\\ mathcal{V}$,
65
+ by virtue of providing access to a basis matrix, a shift vector, and the dimensionality of the trial space,
66
+ while decoupling this representation from *how* it is computed.
67
+
68
+ ####We rely on a tensor representation!
49
69
50
- Like the snapshot data, the basis and the affine offset for a trial space are viewed as tensors,
70
+ Our representation of the basis and the affine offset for a trial space is based on tensors
51
71
$$\\ mathcal{\Phi} \\ in \mathbb{R}^{ N_{\\ mathrm{vars}} \\ times N_{\\ mathrm{x}} \\ times K},$$
52
72
$$\\ mathcal{u}_{\\ mathrm{shift}} \\ in \mathbb{R}^{ N_{\\ mathrm{vars}} \\ times N_{\\ mathrm{x}}}.$$
53
- Here, $N_{\\ mathrm{vars}}$ is the number of PDE variables (e.g., 5 for the compressible Navier-Stokes
54
- equations in 3D), $N_{\\ mathrm{x}}$ is the number of spatial DOFs, and $K$ is the number of basis
55
- vectors. We emphasize that all tensors are reshaped into 2D matrices,
56
- e.g., when performing SVD.
57
- ___
58
- ##**Theory**
73
+ Internally, we remark that all tensors are reshaped into 2D matrices, e.g., when performing SVD.
59
74
75
+ ###**Content**
60
76
61
- A trial space is foundational to reduced-order models.
62
- In a ROM, we restrict a high-dimensional state to live within a low-dimensional trial space.
63
- Mathematically, for a "FOM" vector $\\ mathbf{u} \\ in \\ mathbb{R}^{N_{\\ mathrm{vars}} N_{\\ mathrm{x}}}$, we represent this as
64
- $$\\ mathbf{u} \\ approx \\ tilde{\\ mathbf{u}} \\ in \\ mathcal{V} + \\ mathbf{u}_{\\ mathrm{shift}}$$
65
- where $\\ mathcal{V}$ with
66
- $\\ text{dim}(\\ mathcal{V}) = K \\ le N_{\\ mathrm{vars}} N_{\\ mathrm{x}}$
67
- is the trial space. Formally, we can describe this low-dimensional representation with a basis and an affine offset,
68
- $$\\ tilde{\\ mathbf{u}} = \\ boldsymbol \\ Phi \\ hat{\\ mathbf{u}} + \\ mathbf{u}_{\\ mathrm{shift}}$$
69
- where $\\ boldsymbol \\ Phi \\ in \\ mathbb{R}^{ N_{\\ mathrm{vars}} N_{\\ mathrm{x}} \\ times K}$ is the basis matrix,
70
- $\\ hat{\\ mathbf{u}} \\ in \\ mathbb{R}^{K}$ are the reduced, or generalized coordinates,
71
- $\\ mathbf{u}_{\\ mathrm{shift}} \\ in \\ mathbb{R}^{ N_{\\ mathrm{vars}} N_{\\ mathrm{x}}}$ is the shift vector (or affine offset), and, by definition,
72
- $\\ mathcal{V} \\ equiv \\ mathrm{range}(\\ boldsymbol \\ Phi)$.
77
+ We currently provide the following concrete classes:
78
+
79
+ - `DictionaryTrialSpace`: reduced basis trial space without truncation.
80
+
81
+ - `TrialSpaceFromPOD`: POD trial space computed via SVD.
73
82
74
- The trial_space class encapsulates the information of an affine trial space, $\\ mathcal{V}$,
75
- by virtue of providing access to a basis matrix, a shift vector, and the dimensionality of the trial space.
76
- Note that, like the snapshot data, we view the basis as a tensor.
83
+ - `TrialSpaceFromScaledPOD`: POD trial space computed via scaled SVD.
77
84
78
- ___
85
+ which derive from the abstract class `TrialSpace`. Additionally, we provide two helpers free-functions:
86
+
87
+ - `tensor_to_matrix`: converts a tensor with shape $[N, M, P]$ to a matrix
88
+ representation in which the first two dimension are collapsed $[N M, P]$
89
+
90
+ - `matrix_to_tensor`: inverse operation of `tensor_to_matrix`
91
+
92
+ ---
79
93
##**API**
80
94
'''
81
95
82
-
83
96
import abc
84
97
import numpy as np
85
- from romtools .trial_space_utils .truncater import Truncater , NoOpTruncater
86
- from romtools .trial_space_utils .shifter import Shifter , NoOpShifter
87
- from romtools .trial_space_utils .scaler import Scaler
88
- from romtools .trial_space_utils .splitter import Splitter , NoOpSplitter
89
- from romtools .trial_space_utils .orthogonalizer import Orthogonalizer , NoOpOrthogonalizer
90
-
98
+ from romtools .trial_space .utils .truncater import *
99
+ from romtools .trial_space .utils .shifter import *
100
+ from romtools .trial_space .utils .scaler import *
101
+ from romtools .trial_space .utils .splitter import *
102
+ from romtools .trial_space .utils .orthogonalizer import *
91
103
92
104
class TrialSpace (abc .ABC ):
93
105
'''
94
106
Abstract base class for trial space implementations.
95
107
96
- This abstract class defines the interface for a trial space.
97
-
98
108
Methods:
99
109
'''
100
110
@@ -104,7 +114,7 @@ def get_dimension(self):
104
114
Retrieves the dimension of the trial space
105
115
106
116
Returns:
107
- int: The dimension of the trial space.
117
+ ` int` : The dimension of the trial space.
108
118
109
119
Concrete subclasses should implement this method to return the
110
120
appropriate dimension for their specific trial space implementation.
@@ -117,7 +127,7 @@ def get_shift_vector(self):
117
127
Retrieves the shift vector of the trial space.
118
128
119
129
Returns:
120
- np.ndarray: The shift vector.
130
+ ` np.ndarray` : The shift vector in tensorm form .
121
131
122
132
Concrete subclasses should implement this method to return the shift
123
133
vector specific to their trial space implementation.
@@ -130,38 +140,17 @@ def get_basis(self):
130
140
Retrieves the basis vectors of the trial space.
131
141
132
142
Returns:
133
- np.ndarray: The basis of the trial space.
143
+ ` np.ndarray` : The basis of the trial space in tensor form .
134
144
135
145
Concrete subclasses should implement this method to return the basis
136
146
vectors specific to their trial space implementation.
137
147
'''
138
148
pass
139
149
140
150
141
- def tensor_to_matrix (tensor_input ):
142
- '''
143
- Converts a tensor of snapshots (N_vars x N_space x N_samples) to a matrix
144
- representation in which the first two dimension are collapsed
145
- (N_vars*N_space x N_samples).
146
- '''
147
- output_tensor = tensor_input .reshape (tensor_input .shape [0 ]* tensor_input .shape [1 ],
148
- tensor_input .shape [2 ])
149
- return output_tensor
150
-
151
-
152
- def matrix_to_tensor (n_var , matrix_input ):
153
- '''
154
- Inverse operation of `tensor_to_matrix`
155
- '''
156
- d1 = int (matrix_input .shape [0 ] / n_var )
157
- d2 = matrix_input .shape [1 ]
158
- output_matrix = matrix_input .reshape (n_var , d1 , d2 )
159
- return output_matrix
160
-
161
-
162
151
class DictionaryTrialSpace (TrialSpace ):
163
152
'''
164
- ## Reduced basis trial space (no truncation).
153
+ Reduced basis trial space (no truncation).
165
154
166
155
Given a snapshot matrix $\\ mathbf{S}$, we set the basis to be
167
156
@@ -172,10 +161,10 @@ class DictionaryTrialSpace(TrialSpace):
172
161
'''
173
162
def __init__ (self , snapshots , shifter , splitter , orthogonalizer ):
174
163
'''
175
- Constructor for the reduced basis trial space without truncation .
164
+ Constructor.
176
165
177
166
Args:
178
- snapshots: Snapshot tensor containing solution data
167
+ snapshots (np.ndarray) : Snapshot data in tensor form $\in \mathbb{R}^{ N_{ \\ mathrm{vars}} \\ times N_{ \\ mathrm{x}} \\ times N_{samples}}$
179
168
shifter: Class that shifts the basis.
180
169
splitter: Class that splitts the basis.
181
170
orthogonalizer: Class that orthogonalizes the basis.
@@ -195,36 +184,26 @@ def __init__(self, snapshots, shifter, splitter, orthogonalizer):
195
184
196
185
def get_dimension (self ):
197
186
'''
198
- Retrieves the dimension of trial space
199
-
200
- Returns:
201
- int: The dimension of the trial space.
187
+ Concrete implementation of `TrialSpace.get_dimension()`
202
188
'''
203
189
return self .__dimension
204
190
205
191
def get_shift_vector (self ):
206
192
'''
207
- Retrieves the shift vector
208
-
209
- Returns:
210
- np.ndarray: The shift vector.
211
-
193
+ Concrete implementation of `TrialSpace.get_shift_vector()`
212
194
'''
213
195
return self .__shift_vector
214
196
215
197
def get_basis (self ):
216
198
'''
217
- Retrieves the basis of the trial space
218
-
219
- Returns:
220
- np.ndarray: The basis of the trial space.
199
+ Concrete implementation of `TrialSpace.get_basis()`
221
200
'''
222
201
return self .__basis
223
202
224
203
225
204
class TrialSpaceFromPOD (TrialSpace ):
226
205
'''
227
- ## POD trial space (constructed via SVD).
206
+ POD trial space (constructed via SVD).
228
207
229
208
Given a snapshot matrix $\\ mathbf{S}$, we compute the basis $\\ boldsymbol \\ Phi$ as
230
209
@@ -241,17 +220,17 @@ class TrialSpaceFromPOD(TrialSpace):
241
220
'''
242
221
243
222
def __init__ (self ,
244
- snapshot_tensor ,
223
+ snapshots ,
245
224
truncater : Truncater = NoOpTruncater (),
246
225
shifter : Shifter = NoOpShifter (),
247
226
splitter : Splitter = NoOpSplitter (),
248
227
orthogonalizer : Orthogonalizer = NoOpOrthogonalizer (),
249
228
svdFnc = None ):
250
229
'''
251
- Constructor for the POD trial space .
230
+ Constructor.
252
231
253
232
Args:
254
- snapshot_tensor (np.ndarray): Snapshot data tensor
233
+ snapshots (np.ndarray): Snapshot data in tensor form $\in \mathbb{R}^{ N_{ \\ mathrm{vars}} \\ times N_{ \\ mathrm{x}} \\ times N_{samples}}$
255
234
truncater (Truncater): Class that truncates the basis.
256
235
shifter (Shifter): Class that shifts the basis.
257
236
splitter (Splitter): Class that splits the basis.
@@ -270,9 +249,9 @@ def __init__(self,
270
249
orthogonalization.
271
250
'''
272
251
273
- n_var = snapshot_tensor .shape [0 ]
274
- shifted_snapshot_tensor , self .__shift_vector = shifter (snapshot_tensor )
275
- snapshot_matrix = tensor_to_matrix (shifted_snapshot_tensor )
252
+ n_var = snapshots .shape [0 ]
253
+ shifted_snapshots , self .__shift_vector = shifter (snapshots )
254
+ snapshot_matrix = tensor_to_matrix (shifted_snapshots )
276
255
shifted_split_snapshots = splitter (snapshot_matrix )
277
256
278
257
svd_picked = np .linalg .svd if svdFnc is None else svdFnc
@@ -286,36 +265,26 @@ def __init__(self,
286
265
287
266
def get_dimension (self ):
288
267
'''
289
- Retrieves the dimension of the trial space
290
-
291
- Returns:
292
- int: The dimension of the trial space.
268
+ Concrete implementation of `TrialSpace.get_dimension()`
293
269
'''
294
270
return self .__dimension
295
271
296
272
def get_shift_vector (self ):
297
273
'''
298
- Retrieves the shift vector
299
-
300
- Returns:
301
- np.ndarray: The shift vector.
302
-
274
+ Concrete implementation of `TrialSpace.get_shift_vector()`
303
275
'''
304
276
return self .__shift_vector
305
277
306
278
def get_basis (self ):
307
279
'''
308
- Retrieves the basis of the trial space
309
-
310
- Returns:
311
- np.ndarray: The basis of the trial space.
280
+ Concrete implementation of `TrialSpace.get_basis()`
312
281
'''
313
282
return self .__basis
314
283
315
284
316
285
class TrialSpaceFromScaledPOD (TrialSpace ):
317
286
'''
318
- ## POD trial space (constructed via scaled SVD).
287
+ POD trial space (constructed via scaled SVD).
319
288
320
289
Given a snapshot matrix $\\ mathbf{S}$, we set the basis to be
321
290
@@ -331,17 +300,17 @@ class TrialSpaceFromScaledPOD(TrialSpace):
331
300
truncater.
332
301
'''
333
302
334
- def __init__ (self , snapshot_tensor ,
303
+ def __init__ (self , snapshots ,
335
304
truncater : Truncater ,
336
305
shifter : Shifter ,
337
306
scaler : Scaler ,
338
307
splitter : Splitter ,
339
308
orthogonalizer : Orthogonalizer ):
340
309
'''
341
- Constructor for the POD trial space constructed via scaled SVD .
310
+ Constructor.
342
311
343
312
Args:
344
- snapshot_tensor: np.ndarray snapshot tensor
313
+ snapshots ( np.ndarray): Snapshot data in tensor form $\in \mathbb{R}^{ N_{ \\ mathrm{vars}} \\ times N_{ \\ mathrm{x}} \\ times N_{samples}}$
345
314
truncater: Class that truncates the basis.
346
315
shifter: Class that shifts the basis.
347
316
scaler: Class that scales the basis.
@@ -355,10 +324,10 @@ def __init__(self, snapshot_tensor,
355
324
'''
356
325
357
326
# compute basis
358
- n_var = snapshot_tensor .shape [0 ]
359
- shifted_snapshot_tensor , self .__shift_vector = shifter (snapshot_tensor )
360
- scaled_shifted_snapshot_tensor = scaler .pre_scaling (shifted_snapshot_tensor )
361
- snapshot_matrix = tensor_to_matrix (scaled_shifted_snapshot_tensor )
327
+ n_var = snapshots .shape [0 ]
328
+ shifted_snapshots , self .__shift_vector = shifter (snapshots )
329
+ scaled_shifted_snapshots = scaler .pre_scaling (shifted_snapshots )
330
+ snapshot_matrix = tensor_to_matrix (scaled_shifted_snapshots )
362
331
snapshot_matrix = splitter (snapshot_matrix )
363
332
364
333
lsv , svals , _ = np .linalg .svd (snapshot_matrix , full_matrices = False )
@@ -372,28 +341,38 @@ def __init__(self, snapshot_tensor,
372
341
373
342
def get_dimension (self ):
374
343
'''
375
- Retrieves the dimension of the trial space
376
-
377
- Returns:
378
- int: The dimension of the trial space.
344
+ Concrete implementation of `TrialSpace.get_dimension()`
379
345
'''
380
346
return self .__dimension
381
347
382
348
def get_shift_vector (self ):
383
349
'''
384
- Retrieves the shift vector
385
-
386
- Returns:
387
- np.ndarray: The shift vector.
388
-
350
+ Concrete implementation of `TrialSpace.get_shift_vector()`
389
351
'''
390
352
return self .__shift_vector
391
353
392
354
def get_basis (self ):
393
355
'''
394
- Retrieves the basis of the trial space
395
-
396
- Returns:
397
- np.ndarray: The basis of the trial space.
356
+ Concrete implementation of `TrialSpace.get_basis()`
398
357
'''
399
358
return self .__basis
359
+
360
+
361
+ def tensor_to_matrix (tensor_input ):
362
+ '''
363
+ Converts a tensor with shape $[N, M, P]$ to a matrix representation
364
+ in which the first two dimension are collapsed $[N M, P]$.
365
+ '''
366
+ output_tensor = tensor_input .reshape (tensor_input .shape [0 ]* tensor_input .shape [1 ],
367
+ tensor_input .shape [2 ])
368
+ return output_tensor
369
+
370
+
371
+ def matrix_to_tensor (n_var , matrix_input ):
372
+ '''
373
+ Inverse operation of `tensor_to_matrix`
374
+ '''
375
+ d1 = int (matrix_input .shape [0 ] / n_var )
376
+ d2 = matrix_input .shape [1 ]
377
+ output_matrix = matrix_input .reshape (n_var , d1 , d2 )
378
+ return output_matrix
0 commit comments