PythonicDISORT 1.2__py3-none-any.whl → 1.4__py3-none-any.whl

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.
@@ -1,9 +1,8 @@
1
1
  from PythonicDISORT.subroutines import _mathscr_v
2
- from PythonicDISORT.subroutines import to_diag_ordered_form
3
2
 
4
3
  import numpy as np
4
+ from numpy.lib.stride_tricks import as_strided
5
5
  import scipy as sc
6
- from math import pi
7
6
 
8
7
 
9
8
  def _solve_for_coeffs(
@@ -11,14 +10,14 @@ def _solve_for_coeffs(
11
10
  G_collect, # Eigenvector matrices
12
11
  K_collect, # Eigenvalues
13
12
  B_collect, # Coefficients vectors for particular solutions
14
- G_inv_collect_0, # Inverse of eigenvector matrix for the 0th Fourier mode
13
+ G_inv_mu_inv, # Inverse of eigenvector matrix for the 0th Fourier mode
15
14
  scaled_tau_arr_with_0, # Delta-scaled lower boundary of layers with 0 inserted in front
16
- mu_arr, mu_arr_pos, mu_arr_pos_times_W, # Quadrature nodes for 1) both 2) upper hemispheres; 3) upper hemisphere quadrature nodes times weights
15
+ mu_arr_pos, mu_arr_pos_times_W, # Upper hemisphere quadrature nodes (cosine polar angles); same but multiplied by weights
17
16
  N, NQuad, # Number of 1) upper 2) both hemispheres quadrature nodes
18
17
  NLayers, NBDRF, # Number of 1) layers; 2) BDRF Fourier modes
19
18
  is_atmos_multilayered, # Is the atmosphere multilayered?
20
19
  BDRF_Fourier_modes, # BDRF Fourier modes
21
- mu0, I0, # Properties of the direct beam
20
+ mu0, I0_div_4pi, # Primary properties of the direct beam
22
21
  there_is_beam_source, # Is there a beam source?
23
22
  b_pos, b_neg, # Dirichlet BCs
24
23
  b_pos_is_scalar, b_neg_is_scalar, # Is each Dirichlet BCs scalar and so isotropic?
@@ -44,9 +43,8 @@ def _solve_for_coeffs(
44
43
  | `G_collect` | `NFourier x NLayers x NQuad x NQuad` |
45
44
  | `K_collect` | `NFourier x NLayers x NQuad` |
46
45
  | `B_collect` | `NFourier x NLayers x NQuad` or `None` |
47
- | `G_inv_collect_0` | `NLayers x NQuad x NQuad` or `None` |
46
+ | `G_inv_mu_inv` | `NLayers x NQuad x NQuad` or `None` |
48
47
  | `scaled_tau_arr_with_0` | `NLayers + 1` |
49
- | `mu_arr` | `NQuad` |
50
48
  | `mu_arr_pos` | `NQuad/2` |
51
49
  | `mu_arr_pos_times_W` | `NQuad/2` |
52
50
  | `N` | scalar |
@@ -56,7 +54,7 @@ def _solve_for_coeffs(
56
54
  | `is_atmos_multilayered` | boolean |
57
55
  | `BDRF_Fourier_modes` | `NBDRF` |
58
56
  | `mu0` | scalar |
59
- | `I0` | scalar |
57
+ | `I0_div_4pi` | scalar |
60
58
  | `there_is_beam_source` | boolean |
61
59
  | `b_pos` | `NQuad/2 x NFourier` or `NQuad/2` or scalar |
62
60
  | `b_neg` | `NQuad/2 x NFourier` or `NQuad/2` or scalar |
@@ -78,11 +76,36 @@ def _solve_for_coeffs(
78
76
  ################################## Solve for coefficients of homogeneous solution ##########################################
79
77
  ############# Refer to section 3.6.2 (single-layer) and 4 (multi-layer) of the Comprehensive Documentation #################
80
78
 
81
- dim = NLayers * NQuad
82
- GC_collect = np.empty((NFourier, NLayers, NQuad, NQuad))
83
79
  use_banded_solver = (NLayers >= use_banded_solver_NLayers)
80
+ dim = NLayers * NQuad
81
+ C_collect = np.empty((NFourier, dim))
82
+
83
+ RHS = np.empty(dim)
84
+ i_bot = dim - N # Equals `j_bot_right`
85
+ j_bot_left = dim - NQuad
86
+
84
87
  if use_banded_solver:
85
- Nsupsubdiags = 3 * N - 1 # The number of super-diagonals equals the number of sub-diagonals
88
+ Nsupsubdiags = 3 * N - 1
89
+ LHS_dof = np.zeros((6 * N - 1, dim), order="F") # Diagonal ordered form
90
+ s0, s1 = LHS_dof.strides
91
+ col_stride = s1 - s0 # Stride when moving +1 col and -1 row
92
+
93
+ def _view_slanted(i0, j0, nrows):
94
+ """
95
+ Return a view into `LHS_dof` using slanted mapping.
96
+ The function uses `numpy.lib.stride_tricks.as_strided` which is performant but dangerous,
97
+ which is why this function is not exposed in `PythonicDISORT.subroutines`.
98
+ See https://numpy.org/devdocs/reference/generated/numpy.lib.stride_tricks.as_strided.html.
99
+
100
+ Assume the following exist in the outer scope:
101
+ `LHS_dof`, `Nsupsubdiags`, `s0`, `col_stride`
102
+ """
103
+ r0 = Nsupsubdiags + i0 - j0
104
+ base = LHS_dof[r0 : r0 + nrows, j0 : j0 + N]
105
+ return as_strided(base, shape=(nrows, N), strides=(s0, col_stride))
106
+
107
+ else:
108
+ LHS = np.zeros((dim, dim), order="F")
86
109
 
87
110
  # The following loops can easily be parallelized, but the speed-up is unlikely to be worth the overhead
88
111
  for m in range(NFourier):
@@ -91,8 +114,6 @@ def _solve_for_coeffs(
91
114
 
92
115
  G_collect_m = G_collect[m, :, :, :]
93
116
  K_collect_m = K_collect[m, :, :]
94
- if there_is_beam_source:
95
- B_collect_m = B_collect[m, :, :]
96
117
 
97
118
  # Generate mathscr_D and mathscr_X (BDRF terms)
98
119
  # Just for this part, refer to section 3.4.2 of the Comprehensive Documentation
@@ -103,19 +124,21 @@ def _solve_for_coeffs(
103
124
  mathscr_D_neg = (1 + m_equals_0 * 1) * BDRF_Fourier_modes_m
104
125
  R = mathscr_D_neg * mu_arr_pos_times_W[None, :]
105
126
  if there_is_beam_source:
106
- mathscr_X_pos = (mu0 * I0 / pi) * BDRF_Fourier_modes_m
127
+ mathscr_X_pos = (mu0 * I0_div_4pi * 4) * BDRF_Fourier_modes_m
107
128
  else:
108
129
  mathscr_D_neg = (1 + m_equals_0 * 1) * BDRF_Fourier_modes_m(mu_arr_pos, mu_arr_pos)
109
130
  R = mathscr_D_neg * mu_arr_pos_times_W[None, :]
110
131
  if there_is_beam_source:
111
- mathscr_X_pos = (mu0 * I0 / pi) * BDRF_Fourier_modes_m(
132
+ mathscr_X_pos = (mu0 * I0_div_4pi * 4) * BDRF_Fourier_modes_m(
112
133
  mu_arr_pos, np.array([mu0])
113
134
  )[:, 0]
135
+
114
136
  # --------------------------------------------------------------------------------------------------------------------------
115
137
 
138
+
116
139
  # Assemble RHS
117
140
  # --------------------------------------------------------------------------------------------------------------------------
118
- # Ensure the BCs are of the correct shape
141
+ # Ensure the Dirchlet BCs are of the correct shape
119
142
  if b_pos_is_scalar and m_equals_0:
120
143
  b_pos_m = np.full(N, b_pos)
121
144
  elif b_pos_is_vector and m_equals_0:
@@ -134,32 +157,38 @@ def _solve_for_coeffs(
134
157
  else:
135
158
  b_neg_m = b_neg[:, m]
136
159
 
137
- # _mathscr_v_contribution
160
+ # Top Dirichlet BC
161
+ RHS[:N] = b_neg_m
162
+ # Bottom Dirichlet BC
163
+ RHS[-N:] = b_pos_m
164
+
165
+ # Reset the rest of `RHS` to 0
166
+ RHS[N:-N] = 0
167
+
138
168
  if m_equals_0 and there_is_iso_source:
139
- _mathscr_v_contribution_top = -_mathscr_v(
169
+ # Top boundary
170
+ RHS[:N] -= _mathscr_v(
140
171
  np.array([0]),
141
172
  np.array([0]),
142
173
  Nscoeffs,
143
174
  scaled_s_poly_coeffs[[0], :],
144
175
  G_collect_m[[0], N:, :],
145
176
  K_collect_m[[0], :],
146
- G_inv_collect_0[[0], :, :],
147
- mu_arr,
177
+ G_inv_mu_inv[[0]],
148
178
  ).ravel()
149
179
 
150
- _mathscr_v_contribution_middle = np.array([])
151
180
  if is_atmos_multilayered:
152
181
  indices = np.arange(NLayers - 1)
153
- _mathscr_v_contribution_middle = (
182
+ indicesp1 = indices + 1
183
+ RHS[N:-N] += (
154
184
  _mathscr_v(
155
185
  scaled_tau_arr_with_0[1:-1],
156
186
  indices,
157
187
  Nscoeffs,
158
- scaled_s_poly_coeffs[indices + 1],
159
- G_collect_m[indices + 1],
160
- K_collect_m[indices + 1],
161
- G_inv_collect_0[indices + 1],
162
- mu_arr,
188
+ scaled_s_poly_coeffs[indicesp1],
189
+ G_collect_m[indicesp1],
190
+ K_collect_m[indicesp1],
191
+ G_inv_mu_inv[indicesp1],
163
192
  )
164
193
  - _mathscr_v(
165
194
  scaled_tau_arr_with_0[1:-1],
@@ -168,25 +197,24 @@ def _solve_for_coeffs(
168
197
  scaled_s_poly_coeffs[indices],
169
198
  G_collect_m[indices],
170
199
  K_collect_m[indices],
171
- G_inv_collect_0[indices],
172
- mu_arr,
200
+ G_inv_mu_inv[indices],
173
201
  )
174
202
  ).ravel(order='F')
203
+
204
+ # Bottom boundary
205
+ RHS[-N:] -= _mathscr_v(
206
+ scaled_tau_arr_with_0[[-1]],
207
+ np.array([0]),
208
+ Nscoeffs,
209
+ scaled_s_poly_coeffs[[-1], :],
210
+ G_collect_m[[-1], :N, :],
211
+ K_collect_m[[-1], :],
212
+ G_inv_mu_inv[[-1]],
213
+ ).ravel()
175
214
 
176
- _mathscr_v_contribution_bottom = -_mathscr_v(
177
- scaled_tau_arr_with_0[[-1]],
178
- np.array([0]),
179
- Nscoeffs,
180
- scaled_s_poly_coeffs[[-1], :],
181
- G_collect_m[[-1], :N, :],
182
- K_collect_m[[-1], :],
183
- G_inv_collect_0[[-1], :, :],
184
- mu_arr
185
- ).ravel()
186
- if NBDRF > 0:
187
- _mathscr_v_contribution_bottom = (
188
- _mathscr_v_contribution_bottom
189
- + R
215
+ if there_is_BDRF_mode:
216
+ RHS[-N:] += (
217
+ R
190
218
  @ _mathscr_v(
191
219
  scaled_tau_arr_with_0[[-1]],
192
220
  np.array([0]),
@@ -194,126 +222,164 @@ def _solve_for_coeffs(
194
222
  scaled_s_poly_coeffs[[-1], :],
195
223
  G_collect_m[[-1], N:, :],
196
224
  K_collect_m[[-1], :],
197
- G_inv_collect_0[[-1], :, :],
198
- mu_arr
225
+ G_inv_mu_inv[[-1]],
199
226
  ).ravel()
200
227
  )
201
-
202
- _mathscr_v_contribution = np.concatenate(
203
- [
204
- _mathscr_v_contribution_top,
205
- _mathscr_v_contribution_middle,
206
- _mathscr_v_contribution_bottom,
207
- ]
208
- )
209
- else:
210
- _mathscr_v_contribution = 0
211
228
 
212
229
  if there_is_beam_source:
213
- RHS_middle = np.array([])
230
+ B_collect_m = B_collect[m, :, :]
231
+
232
+ # Top boundary
233
+ RHS[:N] -= B_collect_m[0, N:]
234
+
214
235
  if is_atmos_multilayered:
215
- l_range = np.arange(1, NLayers)
216
- RHS_middle = (
217
- (B_collect_m[l_range, :] - B_collect_m[l_range - 1, :])
218
- * np.exp(-scaled_tau_arr_with_0 / mu0)[l_range, None]
236
+ indicesp1 = np.arange(1, NLayers)
237
+ RHS[N:-N] += (
238
+ (B_collect_m[indicesp1, :] - B_collect_m[indicesp1 - 1, :])
239
+ * np.exp(-scaled_tau_arr_with_0 / mu0)[indicesp1, None]
219
240
  ).ravel()
220
-
241
+
242
+ # Bottom boundary
221
243
  if there_is_BDRF_mode:
222
- RHS = (
223
- np.concatenate(
224
- [
225
- b_neg_m - B_collect_m[0, N:],
226
- RHS_middle,
227
- b_pos_m
228
- + (mathscr_X_pos + R @ B_collect_m[-1, N:] - B_collect_m[-1, :N])
229
- * np.exp(-scaled_tau_arr_with_0[-1] / mu0),
230
- ]
231
- )
232
- + _mathscr_v_contribution
233
- )
244
+ RHS[-N:] += (
245
+ mathscr_X_pos + R @ B_collect_m[-1, N:] - B_collect_m[-1, :N]
246
+ ) * np.exp(-scaled_tau_arr_with_0[-1] / mu0)
247
+
234
248
  else:
235
- RHS = (
236
- np.concatenate(
237
- [
238
- b_neg_m - B_collect_m[0, N:],
239
- RHS_middle,
240
- b_pos_m
241
- - B_collect_m[-1, :N] * np.exp(-scaled_tau_arr_with_0[-1] / mu0),
242
- ]
243
- )
244
- + _mathscr_v_contribution
245
- )
246
- else:
247
- RHS_middle = np.zeros(NQuad * (NLayers - 1))
248
- RHS = np.concatenate([b_neg_m, RHS_middle, b_pos_m]) + _mathscr_v_contribution
249
+ RHS[-N:] -= B_collect_m[-1, :N] * np.exp(-scaled_tau_arr_with_0[-1] / mu0)
250
+
249
251
  # --------------------------------------------------------------------------------------------------------------------------
250
252
 
251
- # Assemble LHS (much of this code is replicated in section 4 of the Comprehensive Documentation)
252
- G_0_nn = G_collect_m[0, N:, :N]
253
- G_0_np = G_collect_m[0, N:, N:]
254
- G_L_pn = G_collect_m[-1, :N, :N]
255
- G_L_nn = G_collect_m[-1, N:, :N]
256
- G_L_pp = G_collect_m[-1, :N, N:]
257
- G_L_np = G_collect_m[-1, N:, N:]
253
+
254
+ # Assemble LHS and solve (much of this code is replicated in section 4 of the Comprehensive Documentation)
255
+ # --------------------------------------------------------------------------------------------------------------------------
256
+ G_0 = G_collect_m[0]
257
+ G_0_nn = G_0[N:, :N]
258
+ G_0_np = G_0[N:, N:]
259
+ E_01 = np.exp(K_collect_m[0, :N] * scaled_tau_arr_with_0[1])[None, :]
260
+
261
+ G_L = G_collect_m[-1]
262
+ G_L_pn = G_L[:N, :N]
263
+ G_L_nn = G_L[N:, :N]
264
+ G_L_pp = G_L[:N, N:]
265
+ G_L_np = G_L[N:, N:]
258
266
  E_Lm1L = np.exp(
259
267
  K_collect_m[-1, :N] * (scaled_tau_arr_with_0[-1] - scaled_tau_arr_with_0[-2])
260
- )
268
+ )[None, :]
261
269
 
262
- LHS = np.zeros((dim, dim))
263
- # BCs for the entire atmosphere
264
- LHS[:N, :N] = G_0_nn
265
- LHS[:N, N : NQuad] = (
266
- G_0_np
267
- * np.exp(K_collect_m[0, :N] * scaled_tau_arr_with_0[1])[None, :]
268
- )
269
- if there_is_BDRF_mode:
270
- LHS[-N:, -NQuad : -N] = (G_L_pn - R @ G_L_nn) * E_Lm1L[None, :]
271
- LHS[-N:, -N:] = G_L_pp - R @ G_L_np
272
- else:
273
- LHS[-N:, -NQuad : -N] = G_L_pn * E_Lm1L[None, :]
274
- LHS[-N:, -N:] = G_L_pp
270
+ ################################## Use `sc.linalg.solve_banded` ##################################
271
+ if use_banded_solver:
272
+
273
+ # Directly assemble diagonal ordered form of LHS matrix
274
+ # ---------------- Top BC ----------------
275
+ G_0 = G_collect_m[0]
276
+ G_0_nn = G_0[N:, :N] # (N,N)
277
+ G_0_np = G_0[N:, N:] # (N,N)
275
278
 
276
- # Interlayer / continuity BCs
277
- for l in range(NLayers - 1):
278
- G_l_pn = G_collect_m[l, :N, :N]
279
- G_l_nn = G_collect_m[l, N:, :N]
280
- G_l_ap = G_collect_m[l, :, N:]
281
- G_lp1_an = G_collect_m[l + 1, :, :N]
282
- G_lp1_pp = G_collect_m[l + 1, :N, N:]
283
- G_lp1_np = G_collect_m[l + 1, N:, N:]
284
- scaled_tau_arr_lm1 = scaled_tau_arr_with_0[l]
285
- scaled_tau_arr_l = scaled_tau_arr_with_0[l + 1]
286
- scaled_tau_arr_lp1 = scaled_tau_arr_with_0[l + 2]
287
- # Postive eigenvalues
288
- K_l_pos = K_collect_m[l, N:]
289
- K_lp1_pos = K_collect_m[l + 1, N:]
290
- E_lm1l = np.exp(K_l_pos * (scaled_tau_arr_lm1 - scaled_tau_arr_l))
291
- E_llp1 = np.exp(K_lp1_pos * (scaled_tau_arr_l - scaled_tau_arr_lp1))
292
-
293
- start_row = N + l * NQuad
294
- start_col = l * NQuad
295
- LHS[start_row : N + start_row, start_col : N + start_col] = G_l_pn * E_lm1l[None, :]
296
- LHS[N + start_row : 2 * N + start_row, start_col : N + start_col] = G_l_nn * E_lm1l[None, :]
297
- LHS[start_row : 2 * N + start_row, N + start_col : 2 * N + start_col] = G_l_ap
298
- LHS[start_row : 2 * N + start_row, 2 * N + start_col : 3 * N + start_col] = -G_lp1_an
299
- LHS[start_row : N + start_row, 3 * N + start_col : 4 * N + start_col] = -G_lp1_pp * E_llp1[None, :]
300
- LHS[N + start_row : 2 * N + start_row, 3 * N + start_col : 4 * N + start_col] = -G_lp1_np * E_llp1[None, :]
301
-
302
- # --------------------------------------------------------------------------------------------------------------------------
279
+ _view_slanted(0, 0, N)[:] = G_0_nn # `_view_slanted` was defined with respect to `LHS_dof`
280
+ _view_slanted(0, N, N)[:] = G_0_np * E_01
281
+
282
+ # ---------------- Bottom BC ----------------
283
+ if there_is_BDRF_mode:
284
+ _view_slanted(i_bot, j_bot_left, N)[:] = (G_L_pn - R @ G_L_nn) * E_Lm1L
285
+ _view_slanted(i_bot, i_bot, N)[:] = G_L_pp - R @ G_L_np
286
+ else:
287
+ _view_slanted(i_bot, j_bot_left, N)[:] = G_L_pn * E_Lm1L
288
+ _view_slanted(i_bot, i_bot, N)[:] = G_L_pp
289
+
290
+ # ---------------- Interlayer / continuity BCs ----------------
291
+ for l in range(NLayers - 1):
292
+ G_l = G_collect_m[l]
293
+ G_l_pn = G_l[:N, :N] # (N,N)
294
+ G_l_nn = G_l[N:, :N] # (N,N)
295
+ G_l_ap = G_l[:, N:] # (2N,N)
296
+
297
+ G_lp1 = G_collect_m[l + 1]
298
+ G_lp1_an = G_lp1[:, :N] # (2N,N)
299
+ G_lp1_pp = G_lp1[:N, N:] # (N,N)
300
+ G_lp1_np = G_lp1[N:, N:] # (N,N)
301
+
302
+ tau_lm1 = scaled_tau_arr_with_0[l]
303
+ tau_l = scaled_tau_arr_with_0[l + 1]
304
+ tau_lp1 = scaled_tau_arr_with_0[l + 2]
305
+
306
+ E_lm1l = np.exp(K_collect_m[l, N:] * (tau_lm1 - tau_l))[None, :] # (N,)
307
+ E_llp1 = np.exp(K_collect_m[l + 1, N:] * (tau_l - tau_lp1))[None, :] # (N,)
308
+
309
+ start_row = N + l * NQuad
310
+ start_col = l * NQuad
311
+
312
+ _view_slanted(start_row, start_col, N)[:] = G_l_pn * E_lm1l
313
+ _view_slanted(start_row + N, start_col, N)[:] = G_l_nn * E_lm1l
314
+ _view_slanted(start_row, start_col + N, NQuad)[:] = G_l_ap
315
+
316
+ _view_slanted(start_row, start_col + NQuad, NQuad)[:] = -G_lp1_an
317
+ _view_slanted(start_row, start_col + 3 * N, N)[:] = -G_lp1_pp * E_llp1
318
+ _view_slanted(start_row + N, start_col + 3 * N, N)[:] = -G_lp1_np * E_llp1
303
319
 
304
- # Solve the system
305
- if use_banded_solver:
320
+ # Solve the system
306
321
  C_m = sc.linalg.solve_banded(
307
322
  (Nsupsubdiags, Nsupsubdiags),
308
- to_diag_ordered_form(LHS, Nsupsubdiags, Nsupsubdiags),
323
+ LHS_dof,
309
324
  RHS,
310
- True,
325
+ False,
311
326
  True,
312
327
  False,
313
328
  )
329
+
330
+ ##################################################################################################
331
+ ##################################### Use `np.linalg.solve` ######################################
314
332
  else:
315
- C_m = np.linalg.solve(LHS, RHS)
333
+
334
+ # ---------------- Top BC ----------------
335
+ LHS[:N, :N] = G_0_nn
336
+ LHS[:N, N : NQuad] = G_0_np * E_01
337
+
338
+ # ---------------- Bottom BC ----------------
339
+ if there_is_BDRF_mode:
340
+ LHS[-N:, -NQuad : -N] = (G_L_pn - R @ G_L_nn) * E_Lm1L
341
+ LHS[-N:, -N:] = G_L_pp - R @ G_L_np
342
+ else:
343
+ LHS[-N:, -NQuad : -N] = G_L_pn * E_Lm1L
344
+ LHS[-N:, -N:] = G_L_pp
316
345
 
317
- GC_collect[m, :, :, :] = G_collect_m * C_m.reshape(NLayers, NQuad)[:, None, :]
346
+ # ---------------- Interlayer / continuity BCs ----------------
347
+ for l in range(NLayers - 1):
348
+ G_l = G_collect_m[l]
349
+ G_l_pn = G_l[:N, :N]
350
+ G_l_nn = G_l[N:, :N]
351
+ G_l_ap = G_l[:, N:]
352
+
353
+ G_lp1 = G_collect_m[l + 1]
354
+ G_lp1_an = G_lp1[:, :N]
355
+ G_lp1_pp = G_lp1[:N, N:]
356
+ G_lp1_np = G_lp1[N:, N:]
357
+
358
+ scaled_tau_arr_lm1 = scaled_tau_arr_with_0[l]
359
+ scaled_tau_arr_l = scaled_tau_arr_with_0[l + 1]
360
+ scaled_tau_arr_lp1 = scaled_tau_arr_with_0[l + 2]
361
+
362
+ # Postive eigenvalues
363
+ E_lm1l = np.exp(K_collect_m[l, N:] * (scaled_tau_arr_lm1 - scaled_tau_arr_l))
364
+ E_llp1 = np.exp(K_collect_m[l + 1, N:] * (scaled_tau_arr_l - scaled_tau_arr_lp1))
365
+
366
+ start_row = N + l * NQuad
367
+ start_col = l * NQuad
368
+
369
+ LHS[start_row : N + start_row, start_col : N + start_col] = G_l_pn * E_lm1l
370
+ LHS[N + start_row : NQuad + start_row, start_col : N + start_col] = G_l_nn * E_lm1l
371
+ LHS[start_row : NQuad + start_row, N + start_col : NQuad + start_col] = G_l_ap
372
+
373
+ LHS[start_row : NQuad + start_row, NQuad + start_col : 3 * N + start_col] = -G_lp1_an
374
+ LHS[start_row : N + start_row, 3 * N + start_col : 4 * N + start_col] = -G_lp1_pp * E_llp1
375
+ LHS[N + start_row : NQuad + start_row, 3 * N + start_col : 4 * N + start_col] = -G_lp1_np * E_llp1
376
+
377
+ # Solve the system
378
+ C_m = np.linalg.solve(LHS, RHS)
379
+
380
+ ##################################################################################################
381
+ # --------------------------------------------------------------------------------------------------------------------------
382
+
383
+ C_collect[m, :] = C_m
318
384
 
319
- return GC_collect
385
+ return G_collect * C_collect.reshape((NFourier, NLayers, 1, NQuad))